@effect-app/vue-components 4.0.0-beta.32 → 4.0.0-beta.320

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.
Files changed (211) hide show
  1. package/README.md +13 -9
  2. package/dist/reset.css +39 -38
  3. package/dist/types/components/CommandButton.vue.d.ts +24 -5
  4. package/dist/types/components/Dialog.vue.d.ts +4 -2
  5. package/dist/types/components/FixedNuxtErrorBoundary.vue.d.ts +39 -0
  6. package/dist/types/components/OmegaForm/InputProps.d.ts +2 -1
  7. package/dist/types/components/OmegaForm/OmegaArray.vue.d.ts +1 -1
  8. package/dist/types/components/OmegaForm/OmegaAutoGen.vue.d.ts +2 -2
  9. package/dist/types/components/OmegaForm/OmegaErrorsInternal.vue.d.ts +2 -3
  10. package/dist/types/components/OmegaForm/OmegaFormInput.vue.d.ts +1 -1
  11. package/dist/types/components/OmegaForm/OmegaInput.vue.d.ts +1 -1
  12. package/dist/types/components/OmegaForm/OmegaInputVuetify.vue.d.ts +12 -0
  13. package/dist/types/components/OmegaForm/OmegaInternalInput.vue.d.ts +7 -9
  14. package/dist/types/components/OmegaForm/OmegaTaggedUnion.vue.d.ts +2 -2
  15. package/dist/types/components/OmegaForm/OmegaTaggedUnionInternal.vue.d.ts +3 -3
  16. package/dist/types/components/OmegaForm/OmegaWrapper.vue.d.ts +1 -1
  17. package/dist/types/components/OmegaForm/createUseFormWithCustomInput.d.ts +4 -2
  18. package/dist/types/components/OmegaForm/decimalSeparatorInput.d.ts +13 -0
  19. package/dist/types/components/OmegaForm/errors.d.ts +33 -0
  20. package/dist/types/components/OmegaForm/getOmegaStore.d.ts +1 -1
  21. package/dist/types/components/OmegaForm/hocs.d.ts +3 -0
  22. package/dist/types/components/OmegaForm/index.d.ts +14 -3
  23. package/dist/types/components/OmegaForm/inputs.d.ts +4 -0
  24. package/dist/types/components/OmegaForm/meta/checks.d.ts +4 -0
  25. package/dist/types/components/OmegaForm/meta/createMeta.d.ts +33 -0
  26. package/dist/types/components/OmegaForm/meta/defaults.d.ts +21 -0
  27. package/dist/types/components/OmegaForm/meta/redacted.d.ts +2 -0
  28. package/dist/types/components/OmegaForm/meta/types.d.ts +56 -0
  29. package/dist/types/components/OmegaForm/meta/walker.d.ts +18 -0
  30. package/dist/types/components/OmegaForm/persistency.d.ts +58 -0
  31. package/dist/types/components/OmegaForm/submit.d.ts +60 -0
  32. package/dist/types/components/OmegaForm/types.d.ts +312 -0
  33. package/dist/types/components/OmegaForm/useOmegaForm.d.ts +9 -213
  34. package/dist/types/components/OmegaForm/validation/localized.d.ts +10 -0
  35. package/dist/types/components/index.d.ts +1 -0
  36. package/dist/types/index.d.ts +0 -1
  37. package/dist/types/utils/index.d.ts +8 -8
  38. package/dist/vue-components.es.js +30 -44
  39. package/dist/vue-components10.es.js +5 -0
  40. package/dist/vue-components11.es.js +56 -0
  41. package/dist/vue-components12.es.js +5 -0
  42. package/dist/vue-components13.es.js +20 -0
  43. package/dist/vue-components14.es.js +50 -0
  44. package/dist/vue-components15.es.js +128 -0
  45. package/dist/vue-components16.es.js +65 -0
  46. package/dist/vue-components17.es.js +114 -0
  47. package/dist/vue-components18.es.js +26 -0
  48. package/dist/vue-components19.es.js +5 -0
  49. package/dist/vue-components2.es.js +11 -0
  50. package/dist/vue-components20.es.js +80 -0
  51. package/dist/vue-components21.es.js +115 -0
  52. package/dist/vue-components22.es.js +74 -0
  53. package/dist/vue-components23.es.js +12 -0
  54. package/dist/vue-components24.es.js +54 -0
  55. package/dist/vue-components25.es.js +5 -0
  56. package/dist/vue-components26.es.js +44 -0
  57. package/dist/vue-components27.es.js +5 -0
  58. package/dist/vue-components28.es.js +84 -0
  59. package/dist/vue-components3.es.js +98 -0
  60. package/dist/vue-components30.es.js +8 -0
  61. package/dist/vue-components31.es.js +9 -0
  62. package/dist/vue-components32.es.js +15 -0
  63. package/dist/vue-components33.es.js +24 -0
  64. package/dist/vue-components34.es.js +344 -0
  65. package/dist/vue-components36.es.js +8 -0
  66. package/dist/vue-components37.es.js +74 -0
  67. package/dist/vue-components38.es.js +5 -0
  68. package/dist/vue-components39.es.js +64 -0
  69. package/dist/vue-components4.es.js +5 -0
  70. package/dist/vue-components40.es.js +5 -0
  71. package/dist/vue-components41.es.js +24 -0
  72. package/dist/vue-components42.es.js +5 -0
  73. package/dist/vue-components43.es.js +59 -0
  74. package/dist/vue-components44.es.js +5 -0
  75. package/dist/vue-components45.es.js +12 -0
  76. package/dist/vue-components46.es.js +22 -0
  77. package/dist/vue-components48.es.js +9 -0
  78. package/dist/vue-components49.es.js +4 -0
  79. package/dist/vue-components5.es.js +24 -0
  80. package/dist/vue-components50.es.js +38 -0
  81. package/dist/vue-components51.es.js +27 -0
  82. package/dist/vue-components52.es.js +28 -0
  83. package/dist/vue-components53.es.js +7 -0
  84. package/dist/vue-components54.es.js +18 -0
  85. package/dist/vue-components55.es.js +36 -0
  86. package/dist/vue-components56.es.js +18 -0
  87. package/dist/vue-components57.es.js +21 -0
  88. package/dist/vue-components58.es.js +30 -0
  89. package/dist/vue-components59.es.js +7 -0
  90. package/dist/vue-components6.es.js +13 -0
  91. package/dist/vue-components60.es.js +9 -0
  92. package/dist/vue-components61.es.js +38 -0
  93. package/dist/vue-components62.es.js +25 -0
  94. package/dist/vue-components63.es.js +128 -0
  95. package/dist/vue-components64.es.js +24 -0
  96. package/dist/vue-components65.es.js +21 -0
  97. package/dist/vue-components66.es.js +9 -0
  98. package/dist/vue-components67.es.js +19 -0
  99. package/dist/vue-components68.es.js +5 -0
  100. package/dist/vue-components69.es.js +29 -0
  101. package/dist/vue-components7.es.js +13 -0
  102. package/dist/vue-components70.es.js +5 -0
  103. package/dist/vue-components71.es.js +43 -0
  104. package/dist/vue-components72.es.js +101 -0
  105. package/dist/vue-components73.es.js +36 -0
  106. package/dist/vue-components74.es.js +19 -0
  107. package/dist/vue-components75.es.js +50 -0
  108. package/dist/vue-components8.es.js +35 -0
  109. package/dist/vue-components9.es.js +47 -0
  110. package/package.json +39 -33
  111. package/src/components/CommandButton.vue +96 -16
  112. package/src/components/Dialog.vue +0 -1
  113. package/src/components/FixedNuxtErrorBoundary.vue +112 -0
  114. package/src/components/OmegaForm/InputProps.ts +2 -1
  115. package/src/components/OmegaForm/OmegaArray.vue +9 -23
  116. package/src/components/OmegaForm/OmegaAutoGen.vue +3 -2
  117. package/src/components/OmegaForm/OmegaErrorsInternal.vue +20 -17
  118. package/src/components/OmegaForm/OmegaFormInput.vue +1 -1
  119. package/src/components/OmegaForm/OmegaInput.vue +29 -43
  120. package/src/components/OmegaForm/OmegaInputVuetify.vue +122 -16
  121. package/src/components/OmegaForm/OmegaInternalInput.vue +33 -13
  122. package/src/components/OmegaForm/OmegaTaggedUnion.vue +10 -3
  123. package/src/components/OmegaForm/OmegaTaggedUnionInternal.vue +6 -6
  124. package/src/components/OmegaForm/OmegaWrapper.vue +1 -1
  125. package/src/components/OmegaForm/blockDialog.ts +18 -6
  126. package/src/components/OmegaForm/createUseFormWithCustomInput.ts +16 -14
  127. package/src/components/OmegaForm/decimalSeparatorInput.ts +38 -0
  128. package/src/components/OmegaForm/errors.ts +139 -0
  129. package/src/components/OmegaForm/getOmegaStore.ts +1 -1
  130. package/src/components/OmegaForm/hocs.ts +19 -0
  131. package/src/components/OmegaForm/index.ts +17 -4
  132. package/src/components/OmegaForm/inputs.ts +22 -0
  133. package/src/components/OmegaForm/meta/checks.ts +107 -0
  134. package/src/components/OmegaForm/meta/createMeta.ts +140 -0
  135. package/src/components/OmegaForm/meta/defaults.ts +263 -0
  136. package/src/components/OmegaForm/meta/redacted.ts +71 -0
  137. package/src/components/OmegaForm/meta/types.ts +78 -0
  138. package/src/components/OmegaForm/meta/walker.ts +248 -0
  139. package/src/components/OmegaForm/persistency.ts +247 -0
  140. package/src/components/OmegaForm/submit.ts +131 -0
  141. package/src/components/OmegaForm/types.ts +801 -0
  142. package/src/components/OmegaForm/useOmegaForm.ts +103 -896
  143. package/src/components/OmegaForm/useRegisterField.ts +1 -1
  144. package/src/components/OmegaForm/validation/localized.ts +238 -0
  145. package/src/components/index.ts +1 -0
  146. package/src/index.ts +0 -1
  147. package/src/reset.css +39 -38
  148. package/src/utils/index.ts +11 -8
  149. package/dist/types/components/OmegaForm/OmegaFormStuff.d.ts +0 -159
  150. package/dist/types/constants/index.d.ts +0 -1
  151. package/dist/vue-components.es10.js +0 -239
  152. package/dist/vue-components.es11.js +0 -32
  153. package/dist/vue-components.es12.js +0 -503
  154. package/dist/vue-components.es13.js +0 -49
  155. package/dist/vue-components.es14.js +0 -4
  156. package/dist/vue-components.es15.js +0 -4
  157. package/dist/vue-components.es16.js +0 -6
  158. package/dist/vue-components.es17.js +0 -13
  159. package/dist/vue-components.es18.js +0 -57
  160. package/dist/vue-components.es19.js +0 -56
  161. package/dist/vue-components.es2.js +0 -30
  162. package/dist/vue-components.es20.js +0 -8
  163. package/dist/vue-components.es21.js +0 -8
  164. package/dist/vue-components.es22.js +0 -5
  165. package/dist/vue-components.es23.js +0 -5
  166. package/dist/vue-components.es24.js +0 -4
  167. package/dist/vue-components.es25.js +0 -4
  168. package/dist/vue-components.es26.js +0 -4
  169. package/dist/vue-components.es27.js +0 -4
  170. package/dist/vue-components.es28.js +0 -19
  171. package/dist/vue-components.es29.js +0 -13
  172. package/dist/vue-components.es3.js +0 -17
  173. package/dist/vue-components.es30.js +0 -31
  174. package/dist/vue-components.es31.js +0 -6
  175. package/dist/vue-components.es32.js +0 -4
  176. package/dist/vue-components.es33.js +0 -4
  177. package/dist/vue-components.es34.js +0 -113
  178. package/dist/vue-components.es36.js +0 -9
  179. package/dist/vue-components.es37.js +0 -34
  180. package/dist/vue-components.es39.js +0 -194
  181. package/dist/vue-components.es4.js +0 -52
  182. package/dist/vue-components.es41.js +0 -6
  183. package/dist/vue-components.es42.js +0 -25
  184. package/dist/vue-components.es43.js +0 -7
  185. package/dist/vue-components.es44.js +0 -23
  186. package/dist/vue-components.es45.js +0 -32
  187. package/dist/vue-components.es46.js +0 -24
  188. package/dist/vue-components.es47.js +0 -14
  189. package/dist/vue-components.es48.js +0 -7
  190. package/dist/vue-components.es49.js +0 -21
  191. package/dist/vue-components.es5.js +0 -52
  192. package/dist/vue-components.es50.js +0 -11
  193. package/dist/vue-components.es51.js +0 -33
  194. package/dist/vue-components.es52.js +0 -50
  195. package/dist/vue-components.es53.js +0 -28
  196. package/dist/vue-components.es54.js +0 -13
  197. package/dist/vue-components.es55.js +0 -67
  198. package/dist/vue-components.es56.js +0 -58
  199. package/dist/vue-components.es57.js +0 -19
  200. package/dist/vue-components.es58.js +0 -35
  201. package/dist/vue-components.es59.js +0 -31
  202. package/dist/vue-components.es6.js +0 -69
  203. package/dist/vue-components.es60.js +0 -44
  204. package/dist/vue-components.es61.js +0 -4
  205. package/dist/vue-components.es62.js +0 -46
  206. package/dist/vue-components.es63.js +0 -4
  207. package/dist/vue-components.es7.js +0 -83
  208. package/dist/vue-components.es8.js +0 -63
  209. package/dist/vue-components.es9.js +0 -21
  210. package/src/components/OmegaForm/OmegaFormStuff.ts +0 -1276
  211. 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
+ }
@@ -1,6 +1,6 @@
1
1
  import { useStore } from "@tanstack/vue-form"
2
2
  import { computed, type Ref } from "vue"
3
- import type { OmegaFormApi, OmegaFormState } from "./OmegaFormStuff"
3
+ import type { OmegaFormApi, OmegaFormState } from "./types"
4
4
 
5
5
  export function getOmegaStore<
6
6
  To,
@@ -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
- export * from "./OmegaFormStuff"
2
- export { type OmegaConfig, type OmegaFormReturn, useOmegaForm } from "./useOmegaForm"
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
+ }