@effect-app/vue-components 4.0.0-beta.32 → 4.0.0-beta.321
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -9
- package/dist/reset.css +39 -38
- package/dist/types/components/CommandButton.vue.d.ts +24 -5
- package/dist/types/components/Dialog.vue.d.ts +4 -2
- package/dist/types/components/FixedNuxtErrorBoundary.vue.d.ts +39 -0
- package/dist/types/components/OmegaForm/InputProps.d.ts +2 -1
- package/dist/types/components/OmegaForm/OmegaArray.vue.d.ts +1 -1
- package/dist/types/components/OmegaForm/OmegaAutoGen.vue.d.ts +2 -2
- package/dist/types/components/OmegaForm/OmegaErrorsInternal.vue.d.ts +2 -3
- package/dist/types/components/OmegaForm/OmegaFormInput.vue.d.ts +1 -1
- package/dist/types/components/OmegaForm/OmegaInput.vue.d.ts +1 -1
- package/dist/types/components/OmegaForm/OmegaInputVuetify.vue.d.ts +12 -0
- package/dist/types/components/OmegaForm/OmegaInternalInput.vue.d.ts +7 -9
- package/dist/types/components/OmegaForm/OmegaTaggedUnion.vue.d.ts +2 -2
- package/dist/types/components/OmegaForm/OmegaTaggedUnionInternal.vue.d.ts +3 -3
- package/dist/types/components/OmegaForm/OmegaWrapper.vue.d.ts +1 -1
- package/dist/types/components/OmegaForm/createUseFormWithCustomInput.d.ts +4 -2
- package/dist/types/components/OmegaForm/decimalSeparatorInput.d.ts +13 -0
- package/dist/types/components/OmegaForm/errors.d.ts +33 -0
- package/dist/types/components/OmegaForm/getOmegaStore.d.ts +1 -1
- package/dist/types/components/OmegaForm/hocs.d.ts +3 -0
- package/dist/types/components/OmegaForm/index.d.ts +14 -3
- package/dist/types/components/OmegaForm/inputs.d.ts +4 -0
- package/dist/types/components/OmegaForm/meta/checks.d.ts +4 -0
- package/dist/types/components/OmegaForm/meta/createMeta.d.ts +33 -0
- package/dist/types/components/OmegaForm/meta/defaults.d.ts +21 -0
- package/dist/types/components/OmegaForm/meta/redacted.d.ts +2 -0
- package/dist/types/components/OmegaForm/meta/types.d.ts +56 -0
- package/dist/types/components/OmegaForm/meta/walker.d.ts +18 -0
- package/dist/types/components/OmegaForm/persistency.d.ts +58 -0
- package/dist/types/components/OmegaForm/submit.d.ts +60 -0
- package/dist/types/components/OmegaForm/types.d.ts +312 -0
- package/dist/types/components/OmegaForm/useOmegaForm.d.ts +9 -213
- package/dist/types/components/OmegaForm/validation/localized.d.ts +10 -0
- package/dist/types/components/index.d.ts +1 -0
- package/dist/types/index.d.ts +0 -1
- package/dist/types/utils/index.d.ts +8 -8
- package/dist/vue-components.es.js +30 -44
- package/dist/vue-components10.es.js +5 -0
- package/dist/vue-components11.es.js +56 -0
- package/dist/vue-components12.es.js +5 -0
- package/dist/vue-components13.es.js +20 -0
- package/dist/vue-components14.es.js +50 -0
- package/dist/vue-components15.es.js +128 -0
- package/dist/vue-components16.es.js +65 -0
- package/dist/vue-components17.es.js +114 -0
- package/dist/vue-components18.es.js +26 -0
- package/dist/vue-components19.es.js +5 -0
- package/dist/vue-components2.es.js +11 -0
- package/dist/vue-components20.es.js +80 -0
- package/dist/vue-components21.es.js +115 -0
- package/dist/vue-components22.es.js +74 -0
- package/dist/vue-components23.es.js +12 -0
- package/dist/vue-components24.es.js +54 -0
- package/dist/vue-components25.es.js +5 -0
- package/dist/vue-components26.es.js +44 -0
- package/dist/vue-components27.es.js +5 -0
- package/dist/vue-components28.es.js +84 -0
- package/dist/vue-components3.es.js +98 -0
- package/dist/vue-components30.es.js +8 -0
- package/dist/vue-components31.es.js +9 -0
- package/dist/vue-components32.es.js +15 -0
- package/dist/vue-components33.es.js +24 -0
- package/dist/vue-components34.es.js +344 -0
- package/dist/vue-components36.es.js +8 -0
- package/dist/vue-components37.es.js +74 -0
- package/dist/vue-components38.es.js +5 -0
- package/dist/vue-components39.es.js +64 -0
- package/dist/vue-components4.es.js +5 -0
- package/dist/vue-components40.es.js +5 -0
- package/dist/vue-components41.es.js +24 -0
- package/dist/vue-components42.es.js +5 -0
- package/dist/vue-components43.es.js +59 -0
- package/dist/vue-components44.es.js +5 -0
- package/dist/vue-components45.es.js +12 -0
- package/dist/vue-components46.es.js +22 -0
- package/dist/vue-components48.es.js +9 -0
- package/dist/vue-components49.es.js +4 -0
- package/dist/vue-components5.es.js +24 -0
- package/dist/vue-components50.es.js +38 -0
- package/dist/vue-components51.es.js +27 -0
- package/dist/vue-components52.es.js +28 -0
- package/dist/vue-components53.es.js +7 -0
- package/dist/vue-components54.es.js +18 -0
- package/dist/vue-components55.es.js +36 -0
- package/dist/vue-components56.es.js +18 -0
- package/dist/vue-components57.es.js +21 -0
- package/dist/vue-components58.es.js +30 -0
- package/dist/vue-components59.es.js +7 -0
- package/dist/vue-components6.es.js +13 -0
- package/dist/vue-components60.es.js +9 -0
- package/dist/vue-components61.es.js +38 -0
- package/dist/vue-components62.es.js +25 -0
- package/dist/vue-components63.es.js +128 -0
- package/dist/vue-components64.es.js +24 -0
- package/dist/vue-components65.es.js +21 -0
- package/dist/vue-components66.es.js +9 -0
- package/dist/vue-components67.es.js +19 -0
- package/dist/vue-components68.es.js +5 -0
- package/dist/vue-components69.es.js +29 -0
- package/dist/vue-components7.es.js +13 -0
- package/dist/vue-components70.es.js +5 -0
- package/dist/vue-components71.es.js +43 -0
- package/dist/vue-components72.es.js +101 -0
- package/dist/vue-components73.es.js +36 -0
- package/dist/vue-components74.es.js +19 -0
- package/dist/vue-components75.es.js +50 -0
- package/dist/vue-components8.es.js +35 -0
- package/dist/vue-components9.es.js +47 -0
- package/package.json +39 -33
- package/src/components/CommandButton.vue +96 -16
- package/src/components/Dialog.vue +0 -1
- package/src/components/FixedNuxtErrorBoundary.vue +112 -0
- package/src/components/OmegaForm/InputProps.ts +2 -1
- package/src/components/OmegaForm/OmegaArray.vue +9 -23
- package/src/components/OmegaForm/OmegaAutoGen.vue +3 -2
- package/src/components/OmegaForm/OmegaErrorsInternal.vue +20 -17
- package/src/components/OmegaForm/OmegaFormInput.vue +1 -1
- package/src/components/OmegaForm/OmegaInput.vue +29 -43
- package/src/components/OmegaForm/OmegaInputVuetify.vue +122 -16
- package/src/components/OmegaForm/OmegaInternalInput.vue +33 -13
- package/src/components/OmegaForm/OmegaTaggedUnion.vue +10 -3
- package/src/components/OmegaForm/OmegaTaggedUnionInternal.vue +6 -6
- package/src/components/OmegaForm/OmegaWrapper.vue +1 -1
- package/src/components/OmegaForm/blockDialog.ts +18 -6
- package/src/components/OmegaForm/createUseFormWithCustomInput.ts +16 -14
- package/src/components/OmegaForm/decimalSeparatorInput.ts +38 -0
- package/src/components/OmegaForm/errors.ts +139 -0
- package/src/components/OmegaForm/getOmegaStore.ts +1 -1
- package/src/components/OmegaForm/hocs.ts +19 -0
- package/src/components/OmegaForm/index.ts +17 -4
- package/src/components/OmegaForm/inputs.ts +22 -0
- package/src/components/OmegaForm/meta/checks.ts +107 -0
- package/src/components/OmegaForm/meta/createMeta.ts +140 -0
- package/src/components/OmegaForm/meta/defaults.ts +263 -0
- package/src/components/OmegaForm/meta/redacted.ts +71 -0
- package/src/components/OmegaForm/meta/types.ts +78 -0
- package/src/components/OmegaForm/meta/walker.ts +248 -0
- package/src/components/OmegaForm/persistency.ts +247 -0
- package/src/components/OmegaForm/submit.ts +131 -0
- package/src/components/OmegaForm/types.ts +801 -0
- package/src/components/OmegaForm/useOmegaForm.ts +103 -896
- package/src/components/OmegaForm/useRegisterField.ts +1 -1
- package/src/components/OmegaForm/validation/localized.ts +242 -0
- package/src/components/index.ts +1 -0
- package/src/index.ts +0 -1
- package/src/reset.css +39 -38
- package/src/utils/index.ts +11 -8
- package/dist/types/components/OmegaForm/OmegaFormStuff.d.ts +0 -159
- package/dist/types/constants/index.d.ts +0 -1
- package/dist/vue-components.es10.js +0 -239
- package/dist/vue-components.es11.js +0 -32
- package/dist/vue-components.es12.js +0 -503
- package/dist/vue-components.es13.js +0 -49
- package/dist/vue-components.es14.js +0 -4
- package/dist/vue-components.es15.js +0 -4
- package/dist/vue-components.es16.js +0 -6
- package/dist/vue-components.es17.js +0 -13
- package/dist/vue-components.es18.js +0 -57
- package/dist/vue-components.es19.js +0 -56
- package/dist/vue-components.es2.js +0 -30
- package/dist/vue-components.es20.js +0 -8
- package/dist/vue-components.es21.js +0 -8
- package/dist/vue-components.es22.js +0 -5
- package/dist/vue-components.es23.js +0 -5
- package/dist/vue-components.es24.js +0 -4
- package/dist/vue-components.es25.js +0 -4
- package/dist/vue-components.es26.js +0 -4
- package/dist/vue-components.es27.js +0 -4
- package/dist/vue-components.es28.js +0 -19
- package/dist/vue-components.es29.js +0 -13
- package/dist/vue-components.es3.js +0 -17
- package/dist/vue-components.es30.js +0 -31
- package/dist/vue-components.es31.js +0 -6
- package/dist/vue-components.es32.js +0 -4
- package/dist/vue-components.es33.js +0 -4
- package/dist/vue-components.es34.js +0 -113
- package/dist/vue-components.es36.js +0 -9
- package/dist/vue-components.es37.js +0 -34
- package/dist/vue-components.es39.js +0 -194
- package/dist/vue-components.es4.js +0 -52
- package/dist/vue-components.es41.js +0 -6
- package/dist/vue-components.es42.js +0 -25
- package/dist/vue-components.es43.js +0 -7
- package/dist/vue-components.es44.js +0 -23
- package/dist/vue-components.es45.js +0 -32
- package/dist/vue-components.es46.js +0 -24
- package/dist/vue-components.es47.js +0 -14
- package/dist/vue-components.es48.js +0 -7
- package/dist/vue-components.es49.js +0 -21
- package/dist/vue-components.es5.js +0 -52
- package/dist/vue-components.es50.js +0 -11
- package/dist/vue-components.es51.js +0 -33
- package/dist/vue-components.es52.js +0 -50
- package/dist/vue-components.es53.js +0 -28
- package/dist/vue-components.es54.js +0 -13
- package/dist/vue-components.es55.js +0 -67
- package/dist/vue-components.es56.js +0 -58
- package/dist/vue-components.es57.js +0 -19
- package/dist/vue-components.es58.js +0 -35
- package/dist/vue-components.es59.js +0 -31
- package/dist/vue-components.es6.js +0 -69
- package/dist/vue-components.es60.js +0 -44
- package/dist/vue-components.es61.js +0 -4
- package/dist/vue-components.es62.js +0 -46
- package/dist/vue-components.es63.js +0 -4
- package/dist/vue-components.es7.js +0 -83
- package/dist/vue-components.es8.js +0 -63
- package/dist/vue-components.es9.js +0 -21
- package/src/components/OmegaForm/OmegaFormStuff.ts +0 -1276
- package/src/constants/index.ts +0 -1
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<component
|
|
3
3
|
:is="form.Field"
|
|
4
|
-
:key="fieldKey"
|
|
5
4
|
:name="name"
|
|
6
|
-
:validators="
|
|
7
|
-
onChange: schema,
|
|
8
|
-
...validators
|
|
9
|
-
}"
|
|
5
|
+
:validators="validators"
|
|
10
6
|
>
|
|
11
|
-
|
|
7
|
+
<!--
|
|
8
|
+
Read `state` from `field.state`, not the Field slot's `state` prop.
|
|
9
|
+
Since @tanstack/vue-form 1.32 the slot `state` is a one-time snapshot
|
|
10
|
+
taken when the field mounts and never updates, whereas `field.state`
|
|
11
|
+
is a reactive getter backed by the field store.
|
|
12
|
+
-->
|
|
13
|
+
<template #default="{ field }">
|
|
12
14
|
<OmegaInternalInput
|
|
13
15
|
v-if="meta"
|
|
14
|
-
v-bind="{ ...$attrs,
|
|
15
|
-
:field="field"
|
|
16
|
-
:state="state"
|
|
16
|
+
v-bind="{ ...$attrs, ...internalInputProps, inputClass: computedClass }"
|
|
17
|
+
:field="field as any"
|
|
18
|
+
:state="field.state"
|
|
17
19
|
:register="form.registerField"
|
|
18
20
|
:label="label ?? errori18n(propsName)"
|
|
19
21
|
:meta="meta"
|
|
@@ -40,14 +42,24 @@
|
|
|
40
42
|
lang="ts"
|
|
41
43
|
generic="From extends Record<PropertyKey, any>, To extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
|
|
42
44
|
>
|
|
45
|
+
/* eslint-disable @typescript-eslint/no-explicit-any -- TanStack Form Field generic interop and slot prop typing */
|
|
43
46
|
import { type DeepKeys } from "@tanstack/vue-form"
|
|
44
47
|
import { computed, inject, type Ref, useAttrs } from "vue"
|
|
45
|
-
import {
|
|
46
|
-
import { type FieldMeta
|
|
48
|
+
import { useErrorLabel } from "./errors"
|
|
49
|
+
import { type FieldMeta } from "./meta/types"
|
|
47
50
|
import OmegaInternalInput from "./OmegaInternalInput.vue"
|
|
48
|
-
import {
|
|
51
|
+
import { type OmegaInputPropsBase } from "./types"
|
|
49
52
|
|
|
50
|
-
const props = defineProps<OmegaInputPropsBase<From, To, Name>>()
|
|
53
|
+
const props = withDefaults(defineProps<OmegaInputPropsBase<From, To, Name>>(), {
|
|
54
|
+
required: undefined
|
|
55
|
+
})
|
|
56
|
+
const internalInputProps = computed(() => ({
|
|
57
|
+
label: props.label,
|
|
58
|
+
required: props.required,
|
|
59
|
+
type: props.type,
|
|
60
|
+
options: props.options,
|
|
61
|
+
inputs: props.form.inputs
|
|
62
|
+
}))
|
|
51
63
|
|
|
52
64
|
// downgrade to *as* DeepKeys<From> to avoid useless and possible infinite recursion in TS
|
|
53
65
|
const propsName = computed(() => props.name as DeepKeys<From>)
|
|
@@ -57,13 +69,10 @@ defineSlots<{
|
|
|
57
69
|
default?: (props: any) => any
|
|
58
70
|
}>()
|
|
59
71
|
|
|
60
|
-
defineOptions({
|
|
61
|
-
inheritAttrs: false
|
|
62
|
-
})
|
|
72
|
+
defineOptions({ inheritAttrs: false })
|
|
63
73
|
|
|
64
74
|
const attrs = useAttrs()
|
|
65
75
|
|
|
66
|
-
// Compute the class to use based on inputClass prop
|
|
67
76
|
const computedClass = computed(() => {
|
|
68
77
|
if (props.inputClass === null) return undefined
|
|
69
78
|
if (props.inputClass !== undefined) return props.inputClass
|
|
@@ -76,33 +85,10 @@ const getMetaFromArray = inject<Ref<(name: string) => FieldMeta | null> | null>(
|
|
|
76
85
|
)
|
|
77
86
|
|
|
78
87
|
const meta = computed(() => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
88
|
+
const fromArray = getMetaFromArray?.value?.(propsName.value)
|
|
89
|
+
if (fromArray) return fromArray
|
|
82
90
|
return props.form.meta[propsName.value]
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// Key to force Field re-mount when meta type changes (for TaggedUnion support)
|
|
86
|
-
const fieldKey = computed(() => {
|
|
87
|
-
const m = meta.value
|
|
88
|
-
if (!m) return propsName.value
|
|
89
|
-
// Include type and key constraints in the key so Field re-mounts when validation rules change
|
|
90
|
-
// Cast to any since not all FieldMeta variants have these properties
|
|
91
|
-
const fm = m as any
|
|
92
|
-
return `${propsName.value}-${fm.type}-${fm.minLength ?? ""}-${fm.maxLength ?? ""}-${fm.minimum ?? ""}-${
|
|
93
|
-
fm.maximum ?? ""
|
|
94
|
-
}`
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
// Call useIntl during setup to avoid issues when computed re-evaluates
|
|
98
|
-
const { trans } = useIntl()
|
|
99
|
-
|
|
100
|
-
const schema = computed(() => {
|
|
101
|
-
if (!meta.value) {
|
|
102
|
-
console.log(props.name, Object.keys(props.form.meta), props.form.meta)
|
|
103
|
-
throw new Error("Meta is undefined")
|
|
104
|
-
}
|
|
105
|
-
return generateInputStandardSchemaFromFieldMeta(meta.value, trans)
|
|
91
|
+
?? props.form.meta[propsName.value.replace(/\[\d+\]/g, "") as DeepKeys<From>]
|
|
106
92
|
})
|
|
107
93
|
|
|
108
94
|
const errori18n = useErrorLabel(props.form)
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
class="omega-input"
|
|
4
|
-
@focusout="
|
|
4
|
+
@focusout="(e) => {
|
|
5
|
+
$emit('blur', e)
|
|
6
|
+
field.handleBlur()
|
|
7
|
+
}"
|
|
5
8
|
@focusin="$emit('focus', $event)"
|
|
6
9
|
>
|
|
7
10
|
<component
|
|
@@ -31,7 +34,12 @@
|
|
|
31
34
|
v-if="inputProps.type === 'email'
|
|
32
35
|
|| inputProps.type === 'string'
|
|
33
36
|
|| inputProps.type === 'password'
|
|
34
|
-
|| inputProps.type === 'date'
|
|
37
|
+
|| inputProps.type === 'date'
|
|
38
|
+
|| inputProps.type === 'tel'
|
|
39
|
+
|| inputProps.type === 'url'
|
|
40
|
+
|| inputProps.type === 'color'
|
|
41
|
+
|| inputProps.type === 'time'
|
|
42
|
+
|| inputProps.type === 'search'"
|
|
35
43
|
:id="inputProps.id"
|
|
36
44
|
:required="inputProps.required"
|
|
37
45
|
:min-length="inputProps.minLength"
|
|
@@ -43,7 +51,7 @@
|
|
|
43
51
|
:error="inputProps.error"
|
|
44
52
|
v-bind="$attrs"
|
|
45
53
|
:model-value="state.value"
|
|
46
|
-
@update:model-value="field.handleChange"
|
|
54
|
+
@update:model-value="(v: any) => field.handleChange(v)"
|
|
47
55
|
>
|
|
48
56
|
<template
|
|
49
57
|
v-if="$slots.label"
|
|
@@ -67,7 +75,7 @@
|
|
|
67
75
|
:error="inputProps.error"
|
|
68
76
|
v-bind="$attrs"
|
|
69
77
|
:model-value="state.value"
|
|
70
|
-
@update:model-value="field.handleChange"
|
|
78
|
+
@update:model-value="(v: any) => field.handleChange(v)"
|
|
71
79
|
>
|
|
72
80
|
<template
|
|
73
81
|
v-if="$slots.label"
|
|
@@ -79,20 +87,54 @@
|
|
|
79
87
|
/>
|
|
80
88
|
</template>
|
|
81
89
|
</v-textarea>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
<!-- min/max are deliberately NOT passed as VNumberInput props: the component
|
|
91
|
+
silently withholds out-of-range values from the model (so schema errors
|
|
92
|
+
would never show) and clamps on blur. Validation stays schema-driven; the
|
|
93
|
+
bounds are exposed to AT via the spinbutton ARIA attrs, which fall through
|
|
94
|
+
to the native input. Same reasoning for precision: Vuetify's default (0)
|
|
95
|
+
would silently block decimals, so it's forced to null and typing 1.5 into
|
|
96
|
+
an int field shows the schema error instead. -->
|
|
97
|
+
<v-number-input
|
|
98
|
+
v-if="inputProps.type === 'number'"
|
|
85
99
|
:id="inputProps.id"
|
|
86
100
|
:required="inputProps.required"
|
|
87
|
-
|
|
88
|
-
:
|
|
89
|
-
:
|
|
101
|
+
role="spinbutton"
|
|
102
|
+
:aria-valuemin="typeof inputProps.min === 'number' ? inputProps.min : undefined"
|
|
103
|
+
:aria-valuemax="typeof inputProps.max === 'number' ? inputProps.max : undefined"
|
|
104
|
+
:aria-valuenow="typeof state.value === 'number' ? state.value : undefined"
|
|
90
105
|
:name="field.name"
|
|
91
106
|
:label="inputProps.label"
|
|
92
107
|
:error-messages="inputProps.errorMessages"
|
|
93
108
|
:error="inputProps.error"
|
|
109
|
+
:precision="null"
|
|
110
|
+
control-variant="stacked"
|
|
94
111
|
v-bind="$attrs"
|
|
95
|
-
:model-value="state.value"
|
|
112
|
+
:model-value="state.value as any"
|
|
113
|
+
@beforeinput.capture="onNumberBeforeinput"
|
|
114
|
+
@update:model-value="(v: number | null) => field.handleChange((v ?? undefined) as any)"
|
|
115
|
+
>
|
|
116
|
+
<template
|
|
117
|
+
v-if="$slots.label"
|
|
118
|
+
#label
|
|
119
|
+
>
|
|
120
|
+
<slot
|
|
121
|
+
name="label"
|
|
122
|
+
v-bind="{ required: inputProps.required, id: inputProps.id, label: inputProps.label }"
|
|
123
|
+
/>
|
|
124
|
+
</template>
|
|
125
|
+
</v-number-input>
|
|
126
|
+
<v-slider
|
|
127
|
+
v-if="inputProps.type === 'range'"
|
|
128
|
+
:id="inputProps.id"
|
|
129
|
+
:required="inputProps.required"
|
|
130
|
+
:min="typeof inputProps.min === 'number' ? inputProps.min : undefined"
|
|
131
|
+
:max="typeof inputProps.max === 'number' ? inputProps.max : undefined"
|
|
132
|
+
:name="field.name"
|
|
133
|
+
:label="inputProps.label"
|
|
134
|
+
:error-messages="inputProps.errorMessages"
|
|
135
|
+
:error="inputProps.error"
|
|
136
|
+
v-bind="$attrs"
|
|
137
|
+
:model-value="state.value as any"
|
|
96
138
|
@update:model-value="(e: any) => {
|
|
97
139
|
if (e || e === 0) {
|
|
98
140
|
field.handleChange(Number(e) as any)
|
|
@@ -110,7 +152,7 @@
|
|
|
110
152
|
v-bind="{ required: inputProps.required, id: inputProps.id, label: inputProps.label }"
|
|
111
153
|
/>
|
|
112
154
|
</template>
|
|
113
|
-
</
|
|
155
|
+
</v-slider>
|
|
114
156
|
<template v-if="inputProps.type === 'radio'">
|
|
115
157
|
<v-radio-group
|
|
116
158
|
:id="inputProps.id"
|
|
@@ -120,7 +162,7 @@
|
|
|
120
162
|
:error="inputProps.error"
|
|
121
163
|
v-bind="$attrs"
|
|
122
164
|
:model-value="state.value"
|
|
123
|
-
@update:model-value="field.handleChange"
|
|
165
|
+
@update:model-value="(v: any) => field.handleChange(v)"
|
|
124
166
|
>
|
|
125
167
|
<template
|
|
126
168
|
v-if="$slots.label"
|
|
@@ -133,7 +175,7 @@
|
|
|
133
175
|
</template>
|
|
134
176
|
<v-radio
|
|
135
177
|
v-for="option in inputProps.options"
|
|
136
|
-
:key="option.value"
|
|
178
|
+
:key="option.value as PropertyKey"
|
|
137
179
|
:label="option.title"
|
|
138
180
|
:value="option.value"
|
|
139
181
|
/>
|
|
@@ -195,6 +237,33 @@
|
|
|
195
237
|
/>
|
|
196
238
|
</template>
|
|
197
239
|
</v-autocomplete>
|
|
240
|
+
|
|
241
|
+
<!-- Fallback for any type not handled by a dedicated branch above (e.g. an
|
|
242
|
+
unrecognized schema type resolved to "unknown", or a custom `type` with
|
|
243
|
+
no renderer). Renders an editable text input instead of nothing. -->
|
|
244
|
+
<v-text-field
|
|
245
|
+
v-if="isUnhandledType"
|
|
246
|
+
:id="inputProps.id"
|
|
247
|
+
:required="inputProps.required"
|
|
248
|
+
:type="getInputType(inputProps.type)"
|
|
249
|
+
:name="field.name"
|
|
250
|
+
:label="inputProps.label"
|
|
251
|
+
:error-messages="inputProps.errorMessages"
|
|
252
|
+
:error="inputProps.error"
|
|
253
|
+
v-bind="$attrs"
|
|
254
|
+
:model-value="state.value"
|
|
255
|
+
@update:model-value="(v: any) => field.handleChange(v)"
|
|
256
|
+
>
|
|
257
|
+
<template
|
|
258
|
+
v-if="$slots.label"
|
|
259
|
+
#label
|
|
260
|
+
>
|
|
261
|
+
<slot
|
|
262
|
+
name="label"
|
|
263
|
+
v-bind="{ required: inputProps.required, id: inputProps.id, label: inputProps.label }"
|
|
264
|
+
/>
|
|
265
|
+
</template>
|
|
266
|
+
</v-text-field>
|
|
198
267
|
</div>
|
|
199
268
|
</template>
|
|
200
269
|
|
|
@@ -204,10 +273,14 @@
|
|
|
204
273
|
generic="From extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
|
|
205
274
|
>
|
|
206
275
|
import { type DeepKeys } from "@tanstack/vue-form"
|
|
207
|
-
import {
|
|
276
|
+
import { computed, useAttrs, watchEffect } from "vue"
|
|
277
|
+
import { useLocale } from "vuetify"
|
|
278
|
+
import { handleDecimalSeparatorBeforeinput } from "./decimalSeparatorInput"
|
|
208
279
|
import type { VuetifyInputProps } from "./InputProps"
|
|
280
|
+
import { getInputType } from "./inputs"
|
|
281
|
+
import { typeOverrides } from "./types"
|
|
209
282
|
|
|
210
|
-
defineProps<VuetifyInputProps<From, Name>>()
|
|
283
|
+
const props = defineProps<VuetifyInputProps<From, Name>>()
|
|
211
284
|
|
|
212
285
|
defineEmits<{
|
|
213
286
|
(e: "focus", event: Event): void
|
|
@@ -217,6 +290,39 @@ defineEmits<{
|
|
|
217
290
|
defineOptions({
|
|
218
291
|
inheritAttrs: false
|
|
219
292
|
})
|
|
293
|
+
|
|
294
|
+
const attrs = useAttrs()
|
|
295
|
+
// useLocale throws outside a Vuetify app (e.g. tests mounting with stubbed
|
|
296
|
+
// vuetify components); numbers then fall back to the "." separator.
|
|
297
|
+
const localeDecimalSeparator = (() => {
|
|
298
|
+
try {
|
|
299
|
+
return useLocale().decimalSeparator
|
|
300
|
+
} catch {
|
|
301
|
+
return undefined
|
|
302
|
+
}
|
|
303
|
+
})()
|
|
304
|
+
|
|
305
|
+
// Mirrors VNumberInput's own resolution: an explicit decimal-separator attr
|
|
306
|
+
// wins, otherwise the locale decides.
|
|
307
|
+
const decimalSeparator = computed(() => {
|
|
308
|
+
const explicit = (attrs["decimal-separator"] ?? attrs["decimalSeparator"]) as string | undefined
|
|
309
|
+
return explicit?.[0] || localeDecimalSeparator?.value || "."
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
const onNumberBeforeinput = (e: Event) => handleDecimalSeparatorBeforeinput(e as InputEvent, decimalSeparator.value)
|
|
313
|
+
|
|
314
|
+
// True when no dedicated branch handles `inputProps.type` (it's outside the
|
|
315
|
+
// built-in `typeOverrides`): the fallback text input renders and we warn.
|
|
316
|
+
const isUnhandledType = computed(() => !(typeOverrides as readonly string[]).includes(props.inputProps.type))
|
|
317
|
+
|
|
318
|
+
watchEffect(() => {
|
|
319
|
+
if (isUnhandledType.value) {
|
|
320
|
+
// eslint-disable-next-line no-console
|
|
321
|
+
console.warn(
|
|
322
|
+
`[OmegaForm] Unhandled input type "${props.inputProps.type}", falling back to a text input.`
|
|
323
|
+
)
|
|
324
|
+
}
|
|
325
|
+
})
|
|
220
326
|
</script>
|
|
221
327
|
|
|
222
328
|
<style>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<slot v-bind="{ ...inputProps.inputProps, field: inputProps.field, state: inputProps.state }">
|
|
3
3
|
<div :class="rootClass">
|
|
4
|
-
<
|
|
5
|
-
|
|
4
|
+
<component
|
|
5
|
+
:is="renderComponent"
|
|
6
|
+
v-if="renderComponent"
|
|
6
7
|
v-bind="{ ...attrsWithoutClass, ...inputProps, class: props.inputClass }"
|
|
7
8
|
>
|
|
8
9
|
<template
|
|
@@ -14,7 +15,7 @@
|
|
|
14
15
|
v-bind="labelProps"
|
|
15
16
|
/>
|
|
16
17
|
</template>
|
|
17
|
-
</
|
|
18
|
+
</component>
|
|
18
19
|
</div>
|
|
19
20
|
</slot>
|
|
20
21
|
</template>
|
|
@@ -24,11 +25,13 @@
|
|
|
24
25
|
lang="ts"
|
|
25
26
|
generic="From extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
|
|
26
27
|
>
|
|
28
|
+
/* eslint-disable @typescript-eslint/no-explicit-any -- TanStack Form / Vue attrs interop */
|
|
27
29
|
import { type DeepKeys, useStore } from "@tanstack/vue-form"
|
|
28
|
-
import { computed, type ComputedRef, getCurrentInstance, useAttrs, useId, useSlots } from "vue"
|
|
30
|
+
import { type Component, computed, type ComputedRef, getCurrentInstance, useAttrs, useId, useSlots } from "vue"
|
|
29
31
|
import type { InputProps, OmegaFieldInternalApi } from "./InputProps"
|
|
30
|
-
import type {
|
|
32
|
+
import type { MetaRecord, NestedKeyOf } from "./meta/types"
|
|
31
33
|
import OmegaInputVuetify from "./OmegaInputVuetify.vue"
|
|
34
|
+
import type { TypeOverride } from "./types"
|
|
32
35
|
|
|
33
36
|
defineOptions({
|
|
34
37
|
inheritAttrs: false
|
|
@@ -41,7 +44,6 @@ const props = withDefaults(
|
|
|
41
44
|
meta: MetaRecord<From>[NestedKeyOf<From>]
|
|
42
45
|
label: string
|
|
43
46
|
type?: TypeOverride
|
|
44
|
-
validators?: FieldValidators<From>
|
|
45
47
|
required?: boolean
|
|
46
48
|
inputClass?: string | null
|
|
47
49
|
|
|
@@ -54,14 +56,16 @@ const props = withDefaults(
|
|
|
54
56
|
) => void
|
|
55
57
|
|
|
56
58
|
// TODO: these should really be optional, depending on the input type (and the custom input type for custom inputs :s)
|
|
57
|
-
options?: { title: string; value:
|
|
59
|
+
options?: { title: string; value: unknown }[]
|
|
60
|
+
|
|
61
|
+
inputs?: Record<string, Component>
|
|
58
62
|
}>(),
|
|
59
63
|
{
|
|
60
64
|
required: undefined,
|
|
61
65
|
type: undefined,
|
|
62
66
|
options: undefined,
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
inputClass: undefined,
|
|
68
|
+
inputs: undefined
|
|
65
69
|
}
|
|
66
70
|
)
|
|
67
71
|
|
|
@@ -84,7 +88,8 @@ const id = useId()
|
|
|
84
88
|
|
|
85
89
|
const fieldApi = props.field
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
// Subscribed for side-effect: keeps component reactive to fieldApi.store changes
|
|
92
|
+
useStore(fieldApi.store, (state) => state)
|
|
88
93
|
|
|
89
94
|
// Get errors from form-level fieldMeta (persists across Field re-mounts)
|
|
90
95
|
const formFieldMeta = useStore(fieldApi.form.store, (state) => state.fieldMeta)
|
|
@@ -98,6 +103,11 @@ const fieldType = computed(() => {
|
|
|
98
103
|
return props.meta?.type || "unknown"
|
|
99
104
|
})
|
|
100
105
|
|
|
106
|
+
const renderComponent = computed(() =>
|
|
107
|
+
props.inputs?.[fieldType.value]
|
|
108
|
+
?? (vuetified ? OmegaInputVuetify : undefined)
|
|
109
|
+
)
|
|
110
|
+
|
|
101
111
|
props.register(computed(() => ({ name: props.field.name, label: props.label, id })))
|
|
102
112
|
|
|
103
113
|
// Get errors from form-level fieldMeta instead of field-level state
|
|
@@ -128,6 +138,13 @@ const handleChange: OmegaFieldInternalApi<From, Name>["handleChange"] = (value)
|
|
|
128
138
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
129
139
|
: null as any
|
|
130
140
|
)
|
|
141
|
+
} else if (props.meta?.isOptionalKey) {
|
|
142
|
+
// `S.optionalKey` expects the key to be ABSENT from the submitted
|
|
143
|
+
// object, not present-with-undefined. Remove it from form state
|
|
144
|
+
// rather than setting it to `undefined`. Note: this is distinct
|
|
145
|
+
// from `required: false`, which may also just mean "empty string
|
|
146
|
+
// is valid" for unconstrained `S.String` fields.
|
|
147
|
+
props.field.form.deleteField(props.field.name)
|
|
131
148
|
} else {
|
|
132
149
|
// Keep the actual value (e.g., empty string for S.String fields)
|
|
133
150
|
props.field.handleChange(value)
|
|
@@ -136,9 +153,11 @@ const handleChange: OmegaFieldInternalApi<From, Name>["handleChange"] = (value)
|
|
|
136
153
|
props.field.handleChange(value)
|
|
137
154
|
}
|
|
138
155
|
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
|
|
156
|
+
// A change here may have materialised a nullable struct (this field is one
|
|
157
|
+
// of its children); backfill the untouched siblings so the live `values`
|
|
158
|
+
// matches what validation and decoding see.
|
|
159
|
+
const form = props.field.form as { normalizeNullableStructs?: () => void }
|
|
160
|
+
form.normalizeNullableStructs?.()
|
|
142
161
|
}
|
|
143
162
|
|
|
144
163
|
// Note: Default value normalization (converting empty strings to null/undefined for nullable fields)
|
|
@@ -172,6 +191,7 @@ const inputProps: ComputedRef<InputProps<From, Name>> = computed(() => ({
|
|
|
172
191
|
? (props.meta?.minimum
|
|
173
192
|
?? (typeof props.meta?.exclusiveMinimum === "number" ? props.meta.exclusiveMinimum + 1 : undefined))
|
|
174
193
|
: undefined,
|
|
194
|
+
refinement: props.meta?.type === "number" ? props.meta?.refinement : undefined,
|
|
175
195
|
errorMessages: errors.value,
|
|
176
196
|
error: !!errors.value.length,
|
|
177
197
|
type: fieldType.value,
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
import { type DeepKeys } from "@tanstack/vue-form"
|
|
9
9
|
import { computed, provide, ref, watch } from "vue"
|
|
10
10
|
import { type TaggedUnionOption } from "./InputProps"
|
|
11
|
-
import { type FieldPath } from "./OmegaFormStuff"
|
|
12
11
|
import OmegaTaggedUnionInternal from "./OmegaTaggedUnionInternal.vue"
|
|
12
|
+
import { type FieldPath } from "./types"
|
|
13
13
|
import { type useOmegaForm } from "./useOmegaForm"
|
|
14
14
|
|
|
15
15
|
const props = defineProps<{
|
|
@@ -30,6 +30,7 @@ watch(
|
|
|
30
30
|
() => {
|
|
31
31
|
const path = tagPath.value
|
|
32
32
|
// Navigate to the nested value
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- traversing arbitrary nested values
|
|
33
34
|
return path.split(".").reduce((acc: any, key) => acc?.[key], formValues.value) as string | null
|
|
34
35
|
},
|
|
35
36
|
(newTag) => {
|
|
@@ -74,9 +75,15 @@ provide("getMetaFromArray", getMetaFromArray)
|
|
|
74
75
|
/>
|
|
75
76
|
</slot>
|
|
76
77
|
<slot />
|
|
78
|
+
<!--
|
|
79
|
+
Drive the branch off `currentTag`: the tag is read from the form store
|
|
80
|
+
(the canonical state), the same single source of truth that feeds
|
|
81
|
+
`getMetaFromArray` above — rather than the `Field` slot's `state`,
|
|
82
|
+
which is only a projection of it.
|
|
83
|
+
-->
|
|
77
84
|
<OmegaTaggedUnionInternal
|
|
78
85
|
:field="inputProps.field as any"
|
|
79
|
-
:
|
|
86
|
+
:tag="currentTag"
|
|
80
87
|
:name="name"
|
|
81
88
|
:form="form"
|
|
82
89
|
>
|
|
@@ -91,7 +98,7 @@ provide("getMetaFromArray", getMetaFromArray)
|
|
|
91
98
|
</template>
|
|
92
99
|
</OmegaTaggedUnionInternal>
|
|
93
100
|
<slot
|
|
94
|
-
v-if="
|
|
101
|
+
v-if="currentTag"
|
|
95
102
|
name="OmegaCommon"
|
|
96
103
|
/>
|
|
97
104
|
</template>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<slot
|
|
3
|
-
v-if="
|
|
4
|
-
:name="`${name ? `${name}.` : ''}${
|
|
5
|
-
v-bind="{ field,
|
|
3
|
+
v-if="tag"
|
|
4
|
+
:name="`${name ? `${name}.` : ''}${tag}`"
|
|
5
|
+
v-bind="{ field, tag }"
|
|
6
6
|
/>
|
|
7
7
|
</template>
|
|
8
8
|
|
|
@@ -17,7 +17,7 @@ import { type OmegaFieldInternalApi } from "./InputProps"
|
|
|
17
17
|
import { type useOmegaForm } from "./useOmegaForm"
|
|
18
18
|
|
|
19
19
|
const props = defineProps<{
|
|
20
|
-
|
|
20
|
+
tag: string | null
|
|
21
21
|
field: OmegaFieldInternalApi<From, Name>
|
|
22
22
|
name?: DeepKeys<From>
|
|
23
23
|
form: ReturnType<typeof useOmegaForm<From, To>>
|
|
@@ -26,7 +26,7 @@ const props = defineProps<{
|
|
|
26
26
|
const values = props.form.useStore(({ values }) => values)
|
|
27
27
|
|
|
28
28
|
// Watch for _tag changes
|
|
29
|
-
watch(() => props.
|
|
29
|
+
watch(() => props.tag, (newTag, oldTag) => {
|
|
30
30
|
if (newTag === null) {
|
|
31
31
|
props.field.setValue(null as DeepValue<From, Name>)
|
|
32
32
|
}
|
|
@@ -34,7 +34,7 @@ watch(() => props.state, (newTag, oldTag) => {
|
|
|
34
34
|
if (newTag !== oldTag) {
|
|
35
35
|
props.form.reset(values.value)
|
|
36
36
|
setTimeout(() => {
|
|
37
|
-
props.field.validate("change")
|
|
37
|
+
void props.field.validate("change")
|
|
38
38
|
}, 0)
|
|
39
39
|
}
|
|
40
40
|
}, { immediate: true })
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
import { useStore } from "@tanstack/vue-form"
|
|
31
31
|
import { usePreventClose } from "./blockDialog"
|
|
32
32
|
import { getOmegaStore } from "./getOmegaStore"
|
|
33
|
-
import { type DefaultTypeProps, type OmegaFormApi, type OmegaFormState } from "./
|
|
33
|
+
import { type DefaultTypeProps, type OmegaFormApi, type OmegaFormState } from "./types"
|
|
34
34
|
import { type OmegaFormReturn } from "./useOmegaForm"
|
|
35
35
|
|
|
36
36
|
type OmegaWrapperProps = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import mitt from "mitt"
|
|
2
2
|
import { inject, type InjectionKey, provide, type Ref } from "vue"
|
|
3
|
+
import { useIntl } from "../../utils"
|
|
3
4
|
import { onMountedWithCleanup } from "./onMountedWithCleanup"
|
|
4
5
|
|
|
5
6
|
export type DialogClosing = { prevent?: boolean | Promise<boolean> }
|
|
@@ -19,11 +20,19 @@ export const usePreventClose = (mkIsDirty: () => Ref<boolean>) => {
|
|
|
19
20
|
if (!bus) {
|
|
20
21
|
return
|
|
21
22
|
}
|
|
23
|
+
const { formatMessage, trans } = useIntl()
|
|
22
24
|
const isDirty = mkIsDirty()
|
|
25
|
+
const defaultMessage = "There are unsaved changes. Are you sure you want to close?"
|
|
23
26
|
onMountedWithCleanup(() => {
|
|
24
27
|
const onDialogClosing = (evt: DialogClosing) => {
|
|
25
28
|
if (isDirty.value) {
|
|
26
|
-
|
|
29
|
+
// Mirror the guard pattern in errors.ts: a custom `useIntl` mock may
|
|
30
|
+
// only provide `trans`, so fall back through trans → defaultMessage.
|
|
31
|
+
const message = formatMessage
|
|
32
|
+
? formatMessage({ id: "form.unsaved_changes_confirm", defaultMessage })
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- key may not be registered in the locale catalog
|
|
34
|
+
: trans?.("form.unsaved_changes_confirm") ?? defaultMessage
|
|
35
|
+
if (!confirm(message)) {
|
|
27
36
|
evt.prevent = true
|
|
28
37
|
}
|
|
29
38
|
}
|
|
@@ -46,11 +55,14 @@ export const useOnClose = (close: () => void) => {
|
|
|
46
55
|
bus.emit("dialog-closing", evt)
|
|
47
56
|
if (evt.prevent) {
|
|
48
57
|
if (typeof evt.prevent === "object" && "then" in evt.prevent) {
|
|
49
|
-
evt
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
evt
|
|
59
|
+
.prevent
|
|
60
|
+
.then((r) => {
|
|
61
|
+
if (r) {
|
|
62
|
+
close()
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
.catch(console.error)
|
|
54
66
|
}
|
|
55
67
|
} else {
|
|
56
68
|
close()
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any -- TanStack Form / Vue render-fn slot interop */
|
|
1
2
|
import type { DeepKeys } from "@tanstack/vue-form"
|
|
2
3
|
import { type Component, h } from "vue"
|
|
3
4
|
import type { MergedInputProps } from "./InputProps"
|
|
4
|
-
import { type DefaultTypeProps } from "./OmegaFormStuff"
|
|
5
5
|
import OmegaInput from "./OmegaInput.vue"
|
|
6
|
+
import type { OmegaConfig } from "./types"
|
|
6
7
|
import { useOmegaForm } from "./useOmegaForm"
|
|
7
8
|
|
|
8
|
-
export const createUseFormWithCustomInput =
|
|
9
|
-
TypeProps = DefaultTypeProps
|
|
10
|
-
>(CustomInputComponent: Component) => {
|
|
9
|
+
export const createUseFormWithCustomInput = (CustomInputComponent: Component) => {
|
|
11
10
|
return <
|
|
12
11
|
From extends Record<PropertyKey, any>,
|
|
13
|
-
To extends Record<PropertyKey, any
|
|
12
|
+
To extends Record<PropertyKey, any>,
|
|
13
|
+
const Cfg extends OmegaConfig<To> = OmegaConfig<To>
|
|
14
14
|
>(
|
|
15
|
-
|
|
15
|
+
schema: Parameters<typeof useOmegaForm<From, To>>[0],
|
|
16
|
+
tanstackFormOptions?: Parameters<typeof useOmegaForm<From, To>>[1],
|
|
17
|
+
omegaConfig?: Cfg
|
|
16
18
|
) => {
|
|
17
|
-
const [schema, tanstackFormOptions, omegaConfig] = args
|
|
18
|
-
|
|
19
19
|
// Create a wrapper that extends OmegaInput and overrides its default slot
|
|
20
20
|
const WrappedInput = {
|
|
21
21
|
name: "WrappedInput",
|
|
@@ -35,7 +35,10 @@ export const createUseFormWithCustomInput = <
|
|
|
35
35
|
&& key !== "form"
|
|
36
36
|
)
|
|
37
37
|
)
|
|
38
|
-
|
|
38
|
+
// A per-type registration (omegaConfig.inputs) wins over the
|
|
39
|
+
// universal CustomInputComponent; otherwise fall back to it.
|
|
40
|
+
const LeafComponent = omegaConfig?.inputs?.[inputProps.type] ?? CustomInputComponent
|
|
41
|
+
return h(LeafComponent, { ...filteredAttrs, field, state, inputProps }, {
|
|
39
42
|
// Pass through label slot if it exists
|
|
40
43
|
...(slots.label && {
|
|
41
44
|
label: (labelProps: any) => slots.label(labelProps)
|
|
@@ -54,13 +57,12 @@ export const createUseFormWithCustomInput = <
|
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
// Thread `Cfg` so `omegaConfig.inputs` keys are inferred into the `type`
|
|
61
|
+
// union (`<form.Input type="...">`), exactly like a bare `useOmegaForm`.
|
|
62
|
+
const form = useOmegaForm<From, To, Cfg>(
|
|
58
63
|
schema,
|
|
59
64
|
tanstackFormOptions,
|
|
60
|
-
{
|
|
61
|
-
...omegaConfig,
|
|
62
|
-
input: WrappedInput
|
|
63
|
-
}
|
|
65
|
+
{ ...omegaConfig, input: WrappedInput } as Cfg
|
|
64
66
|
)
|
|
65
67
|
|
|
66
68
|
return form
|