@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
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const escapeForRegex = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* VNumberInput only accepts the active decimal separator and silently drops
|
|
5
|
+
* the other one while typing. Attached in capture phase on the component
|
|
6
|
+
* root, this handler lets users type either "." or ",": the wrong character
|
|
7
|
+
* is translated to the active separator before Vuetify's own beforeinput
|
|
8
|
+
* filter can reject it.
|
|
9
|
+
*
|
|
10
|
+
* No validation beyond Vuetify's own single-separator typing rule: OmegaForm
|
|
11
|
+
* deliberately lets out-of-schema values through so schema errors show,
|
|
12
|
+
* instead of silently blocking input (same reasoning as min/max not being
|
|
13
|
+
* passed as props).
|
|
14
|
+
*/
|
|
15
|
+
export const handleDecimalSeparatorBeforeinput = (e: InputEvent, separator: string) => {
|
|
16
|
+
if (!e.data) return
|
|
17
|
+
// The active separator may be any single char (locale or explicit prop,
|
|
18
|
+
// e.g. "٫"); everything from [".", ","] that isn't it gets translated.
|
|
19
|
+
const wrongs = [".", ","].filter((c) => c !== separator)
|
|
20
|
+
if (!wrongs.some((c) => e.data!.includes(c))) return
|
|
21
|
+
|
|
22
|
+
const input = e.target as HTMLInputElement
|
|
23
|
+
e.preventDefault()
|
|
24
|
+
e.stopPropagation()
|
|
25
|
+
|
|
26
|
+
const data = wrongs.reduce((acc, c) => acc.replaceAll(c, separator), e.data)
|
|
27
|
+
const start = input.selectionStart ?? input.value.length
|
|
28
|
+
const end = input.selectionEnd ?? input.value.length
|
|
29
|
+
const next = input.value.slice(0, start) + data + input.value.slice(end)
|
|
30
|
+
// Mirrors Vuetify's own typing filter: at most one separator, "-" only at
|
|
31
|
+
// the start. A second separator would only produce an unparseable string.
|
|
32
|
+
if (!new RegExp(`^-?\\d*${escapeForRegex(separator)}?\\d*$`).test(next)) return
|
|
33
|
+
|
|
34
|
+
input.value = next
|
|
35
|
+
const cursor = start + data.length
|
|
36
|
+
input.setSelectionRange(cursor, cursor)
|
|
37
|
+
input.dispatchEvent(new Event("input", { bubbles: true }))
|
|
38
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
|
|
3
|
+
import { type Component, computed, type ComputedRef, type ConcreteComponent, h, onUnmounted, type Ref, ref, watch } from "vue"
|
|
4
|
+
import { useIntl } from "../../utils"
|
|
5
|
+
import type { OmegaError } from "./types"
|
|
6
|
+
import type { OF } from "./useOmegaForm"
|
|
7
|
+
|
|
8
|
+
const normalizePath = (path: string) => path.replace(/\[(\d+)\]/g, ".$1")
|
|
9
|
+
|
|
10
|
+
export const useErrorLabel = (form: OF<any, any>) => {
|
|
11
|
+
const { formatMessage } = useIntl()
|
|
12
|
+
const humanize = (str: string) => {
|
|
13
|
+
return str
|
|
14
|
+
.replace(/([A-Z])/g, " $1") // Add space before capital letters
|
|
15
|
+
.replace(/^./, (char) => char.toUpperCase()) // Capitalize the first letter
|
|
16
|
+
.trim() // Remove leading/trailing spaces
|
|
17
|
+
}
|
|
18
|
+
const fallback = (propsName: string) =>
|
|
19
|
+
formatMessage
|
|
20
|
+
? formatMessage({ id: `general.fields.${propsName}`, defaultMessage: humanize(propsName) })
|
|
21
|
+
: humanize(propsName)
|
|
22
|
+
const i18n = (propsName: string) =>
|
|
23
|
+
form.i18nNamespace
|
|
24
|
+
? formatMessage({ id: `${form.i18nNamespace}.fields.${propsName}`, defaultMessage: fallback(propsName) })
|
|
25
|
+
: fallback(propsName)
|
|
26
|
+
|
|
27
|
+
return i18n
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const eHoc = (errorProps: {
|
|
31
|
+
form: OF<any, any>
|
|
32
|
+
fieldMap: Ref<Map<string, { id: string; label: string }>>
|
|
33
|
+
}) => {
|
|
34
|
+
return function FormHoc<P>(
|
|
35
|
+
WrappedComponent: Component<P>
|
|
36
|
+
): ConcreteComponent<P> {
|
|
37
|
+
return {
|
|
38
|
+
setup() {
|
|
39
|
+
const { fieldMap, form } = errorProps
|
|
40
|
+
const generalErrors = form.useStore((state) => state.errors)
|
|
41
|
+
const fieldMeta = form.useStore((state) => state.fieldMeta)
|
|
42
|
+
const errorMap = form.useStore((state) => state.errorMap)
|
|
43
|
+
|
|
44
|
+
const errorLabel = useErrorLabel(form)
|
|
45
|
+
|
|
46
|
+
const errors = computed(() => {
|
|
47
|
+
// Collect errors from fieldMeta (field-level errors for registered fields)
|
|
48
|
+
const fieldErrors = Object.entries(fieldMeta.value).reduce<OmegaError[]>((acc, [key, m]) => {
|
|
49
|
+
const fieldErrors = (m as { errors?: Array<{ message?: string }> } | undefined)?.errors ?? []
|
|
50
|
+
if (!fieldErrors.length) {
|
|
51
|
+
return acc
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const fieldInfo = fieldMap.value.get(normalizePath(key))
|
|
55
|
+
if (!fieldInfo) {
|
|
56
|
+
return acc
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
acc.push({
|
|
60
|
+
label: fieldInfo.label,
|
|
61
|
+
inputId: fieldInfo.id,
|
|
62
|
+
errors: [fieldErrors[0]?.message].filter(Boolean) as string[]
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
return acc
|
|
66
|
+
}, [])
|
|
67
|
+
|
|
68
|
+
// Collect errors from errorMap.onDynamic / errorMap.onSubmit ONLY for fields that are NOT registered
|
|
69
|
+
// (registered fields already have their errors in fieldMeta).
|
|
70
|
+
// Our localized standard schema writes to onDynamic via validationLogic: revalidateLogic();
|
|
71
|
+
// caller-provided validators.onSubmit (via tanstackFormOptions spread) writes to onSubmit.
|
|
72
|
+
const submitErrors: OmegaError[] = []
|
|
73
|
+
const submitIssueMaps = [errorMap.value.onDynamic, errorMap.value.onSubmit].filter(
|
|
74
|
+
Boolean
|
|
75
|
+
) as unknown as Array<
|
|
76
|
+
Record<string, unknown>
|
|
77
|
+
>
|
|
78
|
+
const seenPaths = new Set<string>()
|
|
79
|
+
for (const issuesByPath of submitIssueMaps) {
|
|
80
|
+
for (const [_, issues] of Object.entries(issuesByPath)) {
|
|
81
|
+
if (Array.isArray(issues) && issues.length) {
|
|
82
|
+
for (const issue of issues) {
|
|
83
|
+
const issAny: any = issue
|
|
84
|
+
if (issAny?.path && Array.isArray(issAny.path) && issAny.path.length) {
|
|
85
|
+
const fieldPath = issAny.path.join(".")
|
|
86
|
+
if (!fieldMap.value.has(fieldPath) && !seenPaths.has(fieldPath)) {
|
|
87
|
+
seenPaths.add(fieldPath)
|
|
88
|
+
submitErrors.push({
|
|
89
|
+
label: errorLabel(fieldPath),
|
|
90
|
+
inputId: fieldPath,
|
|
91
|
+
errors: [issAny.message].filter(Boolean)
|
|
92
|
+
})
|
|
93
|
+
break
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Combine both error sources (no need to check for duplicates since they're mutually exclusive)
|
|
102
|
+
return [...fieldErrors, ...submitErrors]
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
generalErrors,
|
|
107
|
+
errors
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
render({ errors, generalErrors }: any) {
|
|
111
|
+
return h(WrappedComponent, {
|
|
112
|
+
errors,
|
|
113
|
+
generalErrors,
|
|
114
|
+
...this.$attrs
|
|
115
|
+
}, this.$slots)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const makeFieldMap = () => {
|
|
122
|
+
const fieldMap = ref(new Map<string, { label: string; id: string }>())
|
|
123
|
+
const registerField = (field: ComputedRef<{ name: string; label: string; id: string }>) => {
|
|
124
|
+
watch(field, (f) => {
|
|
125
|
+
fieldMap.value.set(normalizePath(f.name), { label: f.label, id: f.id })
|
|
126
|
+
}, { immediate: true })
|
|
127
|
+
onUnmounted(() => {
|
|
128
|
+
// Only delete if we still own this entry (id matches)
|
|
129
|
+
// This prevents old components from deleting entries registered by new components
|
|
130
|
+
// during re-mount transitions (e.g., when :key changes)
|
|
131
|
+
const path = normalizePath(field.value.name)
|
|
132
|
+
const currentEntry = fieldMap.value.get(path)
|
|
133
|
+
if (currentEntry?.id === field.value.id) {
|
|
134
|
+
fieldMap.value.delete(path)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
return { fieldMap, registerField }
|
|
139
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
|
|
3
|
+
import { type Component, type ConcreteComponent, h } from "vue"
|
|
4
|
+
import type { OF } from "./useOmegaForm"
|
|
5
|
+
|
|
6
|
+
export const fHoc = (form: OF<any, any>) => {
|
|
7
|
+
return function FormHoc<P>(
|
|
8
|
+
WrappedComponent: Component<P>
|
|
9
|
+
): ConcreteComponent<P> {
|
|
10
|
+
return {
|
|
11
|
+
render() {
|
|
12
|
+
return h(WrappedComponent, {
|
|
13
|
+
form,
|
|
14
|
+
...this.$attrs
|
|
15
|
+
}, this.$slots)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type * as S from "effect-app/Schema"
|
|
2
|
+
|
|
3
|
+
export { getInputType, type SupportedInputs } from "./inputs"
|
|
4
|
+
export { createMeta, generateMetaFromSchema, isNullableOrUndefined, metadataFromAst } from "./meta/createMeta"
|
|
5
|
+
export type { CreateMeta, FilterItems } from "./meta/createMeta"
|
|
6
|
+
export { defaultsValueFromSchema } from "./meta/defaults"
|
|
7
|
+
export { toFormSchema } from "./meta/redacted"
|
|
8
|
+
export type { BaseFieldMeta, BooleanFieldMeta, DateFieldMeta, FieldMeta, MetaRecord, MultipleFieldMeta, NestedKeyOf, NumberFieldMeta, SelectFieldMeta, StringFieldMeta, UnknownFieldMeta } from "./meta/types"
|
|
9
|
+
export { deepMerge } from "./persistency"
|
|
10
|
+
export type { BaseProps, DefaultTypeProps, FieldPath, FieldPath_, FieldValidators, FormComponent, FormProps, FormType, OmegaArrayProps, OmegaAutoGenMeta, OmegaError, OmegaFormApi, OmegaFormParams, OmegaFormState, OmegaInputProps, OmegaInputPropsBase, PrefixFromDepth, TypeOverride, TypesWithOptions } from "./types"
|
|
11
|
+
export { makeStandardSchemaV1Hooks, toLocalizedStandardSchemaV1 } from "./validation/localized"
|
|
12
|
+
|
|
13
|
+
export { FormErrors, OmegaFormKey, useErrorLabel, useOmegaForm } from "./useOmegaForm"
|
|
14
|
+
export type { defaultValuesPriorityUnion, OF, OmegaConfig, OmegaFormReturn, Policies } from "./useOmegaForm"
|
|
3
15
|
|
|
4
16
|
export { type ExtractTagValue, type ExtractUnionBranch, type InputProps, type MergedInputProps, type TaggedUnionOption, type TaggedUnionOptionsArray, type TaggedUnionProps } from "./InputProps"
|
|
17
|
+
export type { VuetifyInputProps, VuetifyInputProps as OmegaRendererProps } from "./InputProps"
|
|
5
18
|
export { default as OmegaInput } from "./OmegaInput.vue"
|
|
6
19
|
export { default as OmegaVuetifyInput } from "./OmegaInternalInput.vue"
|
|
7
20
|
export { default as OmegaTaggedUnion } from "./OmegaTaggedUnion.vue"
|
|
@@ -9,6 +22,6 @@ export { default as OmegaTaggedUnionInternal } from "./OmegaTaggedUnionInternal.
|
|
|
9
22
|
|
|
10
23
|
export { useOnClose, usePreventClose } from "./blockDialog"
|
|
11
24
|
|
|
12
|
-
export { getInputType } from "./OmegaFormStuff"
|
|
13
|
-
|
|
14
25
|
export { createUseFormWithCustomInput } from "./createUseFormWithCustomInput"
|
|
26
|
+
|
|
27
|
+
export const duplicateSchema = <From, To>(schema: S.Codec<To, From>) => schema
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const supportedInputs = [
|
|
2
|
+
"button",
|
|
3
|
+
"checkbox",
|
|
4
|
+
"color",
|
|
5
|
+
"date",
|
|
6
|
+
"email",
|
|
7
|
+
"number",
|
|
8
|
+
"password",
|
|
9
|
+
"radio",
|
|
10
|
+
"range",
|
|
11
|
+
"search",
|
|
12
|
+
"submit",
|
|
13
|
+
"tel",
|
|
14
|
+
"text",
|
|
15
|
+
"time",
|
|
16
|
+
"url"
|
|
17
|
+
] as const
|
|
18
|
+
|
|
19
|
+
export type SupportedInputs = typeof supportedInputs[number]
|
|
20
|
+
|
|
21
|
+
export const getInputType = (input: string): SupportedInputs =>
|
|
22
|
+
(supportedInputs as readonly string[]).includes(input) ? input as SupportedInputs : "text"
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import * as S from "effect-app/Schema"
|
|
3
|
+
import type * as Record from "effect/Record"
|
|
4
|
+
import type { FieldMeta } from "./types"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Normalise a filter annotation into the meta shape used by field metadata
|
|
8
|
+
* extractors (`{ _tag: "isMinLength", minLength: n }`, …).
|
|
9
|
+
*
|
|
10
|
+
* Since effect beta.91+, filters expose `annotations.representation`
|
|
11
|
+
* (`{ id: "effect/schema/isMinLength", payload: { minLength } }`) instead of
|
|
12
|
+
* the older `annotations.meta` bag.
|
|
13
|
+
*/
|
|
14
|
+
const metaFromFilter = (check: {
|
|
15
|
+
readonly annotations?: {
|
|
16
|
+
readonly meta?: unknown
|
|
17
|
+
readonly representation?: { readonly id?: string; readonly payload?: unknown }
|
|
18
|
+
}
|
|
19
|
+
}): Array<Record<string, any>> => {
|
|
20
|
+
const rep = check.annotations?.representation
|
|
21
|
+
if (rep && typeof rep.id === "string") {
|
|
22
|
+
const tag = rep.id.replace(/^effect\/schema\//, "")
|
|
23
|
+
const payload = rep.payload !== null && typeof rep.payload === "object"
|
|
24
|
+
? rep.payload as Record<string, any>
|
|
25
|
+
: {}
|
|
26
|
+
return [{ _tag: tag, ...payload }]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const meta = check.annotations?.meta
|
|
30
|
+
return meta && typeof meta === "object" ? [meta as Record<string, any>] : []
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const getCheckMetas = (property: S.AST.AST): Array<Record<string, any>> => {
|
|
34
|
+
const checks = property.checks ?? []
|
|
35
|
+
|
|
36
|
+
return checks.flatMap((check) => {
|
|
37
|
+
if (check._tag === "FilterGroup") {
|
|
38
|
+
return check.checks.flatMap((inner) => metaFromFilter(inner))
|
|
39
|
+
}
|
|
40
|
+
return metaFromFilter(check)
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const getFieldMetadataFromAst = (property: S.AST.AST) => {
|
|
45
|
+
const base: Partial<FieldMeta> & Record<string, unknown> = {
|
|
46
|
+
description: S.AST.resolveDescription(property)
|
|
47
|
+
}
|
|
48
|
+
const checks = getCheckMetas(property)
|
|
49
|
+
|
|
50
|
+
if (S.AST.isString(property)) {
|
|
51
|
+
base.type = "string"
|
|
52
|
+
for (const check of checks) {
|
|
53
|
+
switch (check._tag) {
|
|
54
|
+
case "isMinLength":
|
|
55
|
+
base.minLength = check.minLength
|
|
56
|
+
break
|
|
57
|
+
case "isMaxLength":
|
|
58
|
+
base.maxLength = check.maxLength
|
|
59
|
+
break
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const format = property.annotations?.["format"]
|
|
64
|
+
if (format === "email") {
|
|
65
|
+
base.format = "email"
|
|
66
|
+
}
|
|
67
|
+
} else if (S.AST.isNumber(property)) {
|
|
68
|
+
base.type = "number"
|
|
69
|
+
for (const check of checks) {
|
|
70
|
+
switch (check._tag) {
|
|
71
|
+
case "isInt":
|
|
72
|
+
base.refinement = "int"
|
|
73
|
+
break
|
|
74
|
+
case "isGreaterThanOrEqualTo":
|
|
75
|
+
base.minimum = check.minimum
|
|
76
|
+
break
|
|
77
|
+
case "isLessThanOrEqualTo":
|
|
78
|
+
base.maximum = check.maximum
|
|
79
|
+
break
|
|
80
|
+
case "isBetween":
|
|
81
|
+
base.minimum = check.minimum
|
|
82
|
+
base.maximum = check.maximum
|
|
83
|
+
break
|
|
84
|
+
case "isGreaterThan":
|
|
85
|
+
base.exclusiveMinimum = check.exclusiveMinimum
|
|
86
|
+
break
|
|
87
|
+
case "isLessThan":
|
|
88
|
+
base.exclusiveMaximum = check.exclusiveMaximum
|
|
89
|
+
break
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} else if (S.AST.isBoolean(property)) {
|
|
93
|
+
base.type = "boolean"
|
|
94
|
+
} else if (
|
|
95
|
+
S.AST.isDeclaration(property)
|
|
96
|
+
&& (
|
|
97
|
+
(property.annotations as any)?.typeConstructor?._tag === "Date"
|
|
98
|
+
|| (property.annotations as any)?.representation?.id === "effect/schema/Date"
|
|
99
|
+
)
|
|
100
|
+
) {
|
|
101
|
+
base.type = "date"
|
|
102
|
+
} else {
|
|
103
|
+
base.type = "unknown"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return base
|
|
107
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import type * as Effect from "effect-app/Effect"
|
|
3
|
+
import * as S from "effect-app/Schema"
|
|
4
|
+
import type * as Record from "effect/Record"
|
|
5
|
+
import { getTransformationFrom } from "../../../utils"
|
|
6
|
+
import type { FieldMeta, MetaRecord } from "./types"
|
|
7
|
+
import { classifyAndWalkUnion, leafMetaForAst, type ParentMeta, type WalkerContext, walkStruct } from "./walker"
|
|
8
|
+
|
|
9
|
+
export type FilterItems = {
|
|
10
|
+
items: readonly [string, ...string[]]
|
|
11
|
+
message:
|
|
12
|
+
| string
|
|
13
|
+
| Effect.Effect<string>
|
|
14
|
+
| { readonly message: string | Effect.Effect<string> }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type CreateMeta =
|
|
18
|
+
& {
|
|
19
|
+
parent?: string
|
|
20
|
+
meta?: Record<string, any>
|
|
21
|
+
nullableOrUndefined?: false | "undefined" | "null"
|
|
22
|
+
}
|
|
23
|
+
& (
|
|
24
|
+
| {
|
|
25
|
+
propertySignatures: readonly S.AST.PropertySignature[]
|
|
26
|
+
property?: never
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
propertySignatures?: never
|
|
30
|
+
property: S.AST.AST
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
export const unwrapDeclaration = (property: S.AST.AST): S.AST.AST => {
|
|
35
|
+
let current = getTransformationFrom(property)
|
|
36
|
+
|
|
37
|
+
while (S.AST.isDeclaration(current) && current.typeParameters.length > 0) {
|
|
38
|
+
current = getTransformationFrom(current.typeParameters[0])
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return current
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const isNullableOrUndefined = (property: false | S.AST.AST | undefined) => {
|
|
45
|
+
if (!property || !S.AST.isUnion(property)) return false
|
|
46
|
+
if (property.types.find((_) => S.AST.isUndefined(_))) {
|
|
47
|
+
return "undefined"
|
|
48
|
+
}
|
|
49
|
+
if (property.types.find((_) => S.AST.isNull(_))) return "null"
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const createMeta = <T = any>(
|
|
54
|
+
{ meta = {}, parent = "", property, propertySignatures }: CreateMeta,
|
|
55
|
+
acc: Partial<MetaRecord<T>> = {}
|
|
56
|
+
): MetaRecord<T> | FieldMeta => {
|
|
57
|
+
const ctx: WalkerContext<T> = { acc, unionMeta: {} }
|
|
58
|
+
|
|
59
|
+
if (propertySignatures) {
|
|
60
|
+
const parentMeta: ParentMeta = {
|
|
61
|
+
required: meta.required !== false,
|
|
62
|
+
nullableOrUndefined: meta.nullableOrUndefined ?? false
|
|
63
|
+
}
|
|
64
|
+
walkStruct(propertySignatures, parent, parentMeta, ctx)
|
|
65
|
+
return acc
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (property) {
|
|
69
|
+
const nullableOrUndefined = isNullableOrUndefined(property)
|
|
70
|
+
const unwrapped = unwrapDeclaration(property)
|
|
71
|
+
const required = !Object.hasOwnProperty.call(meta, "required")
|
|
72
|
+
? !nullableOrUndefined
|
|
73
|
+
: (meta.required as boolean)
|
|
74
|
+
|
|
75
|
+
const parentMeta: ParentMeta = {
|
|
76
|
+
required,
|
|
77
|
+
nullableOrUndefined: (meta.nullableOrUndefined ?? nullableOrUndefined) as false | "null" | "undefined"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (S.AST.isObjects(unwrapped)) {
|
|
81
|
+
walkStruct(unwrapped.propertySignatures, parent, parentMeta, ctx)
|
|
82
|
+
return acc
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (S.AST.isUnion(unwrapped)) {
|
|
86
|
+
// For property-mode, return a FieldMeta by running through classifyAndWalkUnion
|
|
87
|
+
// and then pulling out the result at `parent` key
|
|
88
|
+
const leafCtx: WalkerContext<T> = { acc: {}, unionMeta: {} }
|
|
89
|
+
classifyAndWalkUnion(unwrapped, parent, parentMeta, leafCtx)
|
|
90
|
+
const result = (leafCtx.acc as any)[parent]
|
|
91
|
+
if (result) return result as FieldMeta
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return leafMetaForAst(unwrapped, parentMeta)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return acc
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const metadataFromAst = <From, To>(
|
|
101
|
+
schema: S.Codec<To, From>
|
|
102
|
+
): {
|
|
103
|
+
meta: MetaRecord<To>
|
|
104
|
+
defaultValues: Record<string, any>
|
|
105
|
+
unionMeta: Record<string, MetaRecord<To>>
|
|
106
|
+
} => {
|
|
107
|
+
const ast = unwrapDeclaration(schema.ast)
|
|
108
|
+
const newMeta: Partial<MetaRecord<To>> = {}
|
|
109
|
+
const defaultValues: Record<string, any> = {}
|
|
110
|
+
const unionMeta: Record<string, MetaRecord<To>> = {}
|
|
111
|
+
|
|
112
|
+
const ctx: WalkerContext<To> = { acc: newMeta, unionMeta }
|
|
113
|
+
|
|
114
|
+
if (S.AST.isUnion(ast)) {
|
|
115
|
+
// Root-level discriminated union
|
|
116
|
+
classifyAndWalkUnion(ast, "", { required: true, nullableOrUndefined: false }, ctx)
|
|
117
|
+
|
|
118
|
+
return { meta: newMeta, defaultValues, unionMeta }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (S.AST.isObjects(ast)) {
|
|
122
|
+
walkStruct(ast.propertySignatures, "", { required: true, nullableOrUndefined: false }, ctx)
|
|
123
|
+
|
|
124
|
+
return { meta: newMeta, defaultValues, unionMeta }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { meta: newMeta, defaultValues, unionMeta }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const generateMetaFromSchema = <From, To>(
|
|
131
|
+
schema: S.Codec<To, From>
|
|
132
|
+
): {
|
|
133
|
+
schema: S.Codec<To, From>
|
|
134
|
+
meta: MetaRecord<To>
|
|
135
|
+
unionMeta: Record<string, MetaRecord<To>>
|
|
136
|
+
} => {
|
|
137
|
+
const { meta, unionMeta } = metadataFromAst(schema)
|
|
138
|
+
|
|
139
|
+
return { schema, meta, unionMeta }
|
|
140
|
+
}
|