@effect-app/vue-components 4.0.0-beta.18 → 4.0.0-beta.180

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 (195) hide show
  1. package/README.md +36 -8
  2. package/dist/reset.css +52 -0
  3. package/dist/types/components/OmegaForm/OmegaArray.vue.d.ts +1 -1
  4. package/dist/types/components/OmegaForm/OmegaAutoGen.vue.d.ts +2 -3
  5. package/dist/types/components/OmegaForm/OmegaErrorsInternal.vue.d.ts +1 -1
  6. package/dist/types/components/OmegaForm/OmegaFormInput.vue.d.ts +1 -1
  7. package/dist/types/components/OmegaForm/OmegaInput.vue.d.ts +1 -1
  8. package/dist/types/components/OmegaForm/OmegaInternalInput.vue.d.ts +2 -1
  9. package/dist/types/components/OmegaForm/OmegaWrapper.vue.d.ts +1 -1
  10. package/dist/types/components/OmegaForm/createUseFormWithCustomInput.d.ts +2 -2
  11. package/dist/types/components/OmegaForm/errors.d.ts +33 -0
  12. package/dist/types/components/OmegaForm/getOmegaStore.d.ts +1 -1
  13. package/dist/types/components/OmegaForm/hocs.d.ts +3 -0
  14. package/dist/types/components/OmegaForm/index.d.ts +13 -3
  15. package/dist/types/components/OmegaForm/inputs.d.ts +4 -0
  16. package/dist/types/components/OmegaForm/meta/checks.d.ts +4 -0
  17. package/dist/types/components/OmegaForm/meta/createMeta.d.ts +32 -0
  18. package/dist/types/components/OmegaForm/meta/defaults.d.ts +2 -0
  19. package/dist/types/components/OmegaForm/meta/redacted.d.ts +2 -0
  20. package/dist/types/components/OmegaForm/meta/types.d.ts +56 -0
  21. package/dist/types/components/OmegaForm/meta/walker.d.ts +18 -0
  22. package/dist/types/components/OmegaForm/persistency.d.ts +58 -0
  23. package/dist/types/components/OmegaForm/submit.d.ts +60 -0
  24. package/dist/types/components/OmegaForm/types.d.ts +281 -0
  25. package/dist/types/components/OmegaForm/useOmegaForm.d.ts +6 -212
  26. package/dist/types/components/OmegaForm/validation/localized.d.ts +10 -0
  27. package/dist/types/index.d.ts +0 -1
  28. package/dist/types/utils/index.d.ts +6 -6
  29. package/dist/vue-components.es.js +29 -45
  30. package/dist/vue-components10.es.js +5 -0
  31. package/dist/vue-components11.es.js +20 -0
  32. package/dist/vue-components12.es.js +49 -0
  33. package/dist/vue-components13.es.js +127 -0
  34. package/dist/vue-components14.es.js +65 -0
  35. package/dist/vue-components15.es.js +60 -0
  36. package/dist/vue-components16.es.js +22 -0
  37. package/dist/vue-components17.es.js +5 -0
  38. package/dist/vue-components18.es.js +80 -0
  39. package/dist/vue-components19.es.js +92 -0
  40. package/dist/vue-components2.es.js +11 -0
  41. package/dist/vue-components20.es.js +73 -0
  42. package/dist/vue-components21.es.js +12 -0
  43. package/dist/vue-components22.es.js +56 -0
  44. package/dist/vue-components23.es.js +5 -0
  45. package/dist/vue-components24.es.js +44 -0
  46. package/dist/vue-components25.es.js +5 -0
  47. package/dist/vue-components26.es.js +84 -0
  48. package/dist/vue-components28.es.js +8 -0
  49. package/dist/vue-components29.es.js +9 -0
  50. package/dist/vue-components3.es.js +54 -0
  51. package/dist/vue-components30.es.js +269 -0
  52. package/dist/vue-components32.es.js +8 -0
  53. package/dist/vue-components33.es.js +73 -0
  54. package/dist/vue-components34.es.js +5 -0
  55. package/dist/vue-components35.es.js +52 -0
  56. package/dist/vue-components36.es.js +5 -0
  57. package/dist/vue-components37.es.js +24 -0
  58. package/dist/vue-components38.es.js +5 -0
  59. package/dist/vue-components39.es.js +59 -0
  60. package/dist/vue-components4.es.js +5 -0
  61. package/dist/vue-components40.es.js +5 -0
  62. package/dist/vue-components41.es.js +12 -0
  63. package/dist/vue-components42.es.js +22 -0
  64. package/dist/vue-components44.es.js +9 -0
  65. package/dist/vue-components45.es.js +4 -0
  66. package/dist/vue-components46.es.js +38 -0
  67. package/dist/vue-components47.es.js +27 -0
  68. package/dist/vue-components48.es.js +28 -0
  69. package/dist/vue-components49.es.js +7 -0
  70. package/dist/vue-components5.es.js +24 -0
  71. package/dist/vue-components50.es.js +18 -0
  72. package/dist/vue-components51.es.js +36 -0
  73. package/dist/vue-components52.es.js +18 -0
  74. package/dist/vue-components53.es.js +21 -0
  75. package/dist/vue-components54.es.js +30 -0
  76. package/dist/vue-components55.es.js +7 -0
  77. package/dist/vue-components56.es.js +9 -0
  78. package/dist/vue-components57.es.js +38 -0
  79. package/dist/vue-components58.es.js +25 -0
  80. package/dist/vue-components59.es.js +128 -0
  81. package/dist/vue-components6.es.js +13 -0
  82. package/dist/vue-components60.es.js +24 -0
  83. package/dist/vue-components61.es.js +21 -0
  84. package/dist/vue-components62.es.js +9 -0
  85. package/dist/vue-components63.es.js +19 -0
  86. package/dist/vue-components64.es.js +5 -0
  87. package/dist/vue-components65.es.js +29 -0
  88. package/dist/vue-components66.es.js +5 -0
  89. package/dist/vue-components67.es.js +29 -0
  90. package/dist/vue-components68.es.js +6 -0
  91. package/dist/vue-components69.es.js +18 -0
  92. package/dist/vue-components7.es.js +13 -0
  93. package/dist/vue-components70.es.js +40 -0
  94. package/dist/vue-components71.es.js +81 -0
  95. package/dist/vue-components72.es.js +33 -0
  96. package/dist/vue-components73.es.js +19 -0
  97. package/dist/vue-components74.es.js +48 -0
  98. package/dist/vue-components8.es.js +35 -0
  99. package/dist/vue-components9.es.js +47 -0
  100. package/package.json +34 -30
  101. package/src/components/CommandButton.vue +4 -2
  102. package/src/components/OmegaForm/OmegaArray.vue +7 -5
  103. package/src/components/OmegaForm/OmegaAutoGen.vue +5 -3
  104. package/src/components/OmegaForm/OmegaErrorsInternal.vue +1 -1
  105. package/src/components/OmegaForm/OmegaFormInput.vue +6 -2
  106. package/src/components/OmegaForm/OmegaInput.vue +12 -37
  107. package/src/components/OmegaForm/OmegaInputVuetify.vue +5 -2
  108. package/src/components/OmegaForm/OmegaInternalInput.vue +18 -10
  109. package/src/components/OmegaForm/OmegaTaggedUnion.vue +6 -3
  110. package/src/components/OmegaForm/OmegaTaggedUnionInternal.vue +5 -1
  111. package/src/components/OmegaForm/OmegaWrapper.vue +5 -3
  112. package/src/components/OmegaForm/blockDialog.ts +10 -1
  113. package/src/components/OmegaForm/createUseFormWithCustomInput.ts +2 -1
  114. package/src/components/OmegaForm/errors.ts +136 -0
  115. package/src/components/OmegaForm/getOmegaStore.ts +1 -1
  116. package/src/components/OmegaForm/hocs.ts +19 -0
  117. package/src/components/OmegaForm/index.ts +16 -4
  118. package/src/components/OmegaForm/inputs.ts +22 -0
  119. package/src/components/OmegaForm/meta/checks.ts +81 -0
  120. package/src/components/OmegaForm/meta/createMeta.ts +138 -0
  121. package/src/components/OmegaForm/meta/defaults.ts +132 -0
  122. package/src/components/OmegaForm/meta/redacted.ts +66 -0
  123. package/src/components/OmegaForm/meta/types.ts +78 -0
  124. package/src/components/OmegaForm/meta/walker.ts +247 -0
  125. package/src/components/OmegaForm/persistency.ts +247 -0
  126. package/src/components/OmegaForm/submit.ts +128 -0
  127. package/src/components/OmegaForm/types.ts +751 -0
  128. package/src/components/OmegaForm/useOmegaForm.ts +57 -892
  129. package/src/components/OmegaForm/validation/localized.ts +202 -0
  130. package/src/index.ts +0 -1
  131. package/src/reset.css +52 -0
  132. package/src/utils/index.ts +10 -7
  133. package/dist/types/components/OmegaForm/OmegaFormStuff.d.ts +0 -157
  134. package/dist/types/constants/index.d.ts +0 -1
  135. package/dist/vue-components.es10.js +0 -239
  136. package/dist/vue-components.es11.js +0 -32
  137. package/dist/vue-components.es12.js +0 -481
  138. package/dist/vue-components.es13.js +0 -49
  139. package/dist/vue-components.es14.js +0 -4
  140. package/dist/vue-components.es15.js +0 -4
  141. package/dist/vue-components.es16.js +0 -6
  142. package/dist/vue-components.es17.js +0 -13
  143. package/dist/vue-components.es18.js +0 -57
  144. package/dist/vue-components.es19.js +0 -56
  145. package/dist/vue-components.es2.js +0 -31
  146. package/dist/vue-components.es20.js +0 -8
  147. package/dist/vue-components.es21.js +0 -8
  148. package/dist/vue-components.es22.js +0 -5
  149. package/dist/vue-components.es23.js +0 -5
  150. package/dist/vue-components.es24.js +0 -4
  151. package/dist/vue-components.es25.js +0 -4
  152. package/dist/vue-components.es26.js +0 -4
  153. package/dist/vue-components.es27.js +0 -4
  154. package/dist/vue-components.es28.js +0 -19
  155. package/dist/vue-components.es29.js +0 -13
  156. package/dist/vue-components.es3.js +0 -17
  157. package/dist/vue-components.es30.js +0 -31
  158. package/dist/vue-components.es31.js +0 -6
  159. package/dist/vue-components.es32.js +0 -4
  160. package/dist/vue-components.es33.js +0 -4
  161. package/dist/vue-components.es34.js +0 -113
  162. package/dist/vue-components.es36.js +0 -9
  163. package/dist/vue-components.es37.js +0 -34
  164. package/dist/vue-components.es39.js +0 -194
  165. package/dist/vue-components.es4.js +0 -52
  166. package/dist/vue-components.es41.js +0 -6
  167. package/dist/vue-components.es42.js +0 -25
  168. package/dist/vue-components.es43.js +0 -7
  169. package/dist/vue-components.es44.js +0 -23
  170. package/dist/vue-components.es45.js +0 -32
  171. package/dist/vue-components.es46.js +0 -24
  172. package/dist/vue-components.es47.js +0 -14
  173. package/dist/vue-components.es48.js +0 -7
  174. package/dist/vue-components.es49.js +0 -21
  175. package/dist/vue-components.es5.js +0 -52
  176. package/dist/vue-components.es50.js +0 -11
  177. package/dist/vue-components.es51.js +0 -33
  178. package/dist/vue-components.es52.js +0 -50
  179. package/dist/vue-components.es53.js +0 -28
  180. package/dist/vue-components.es54.js +0 -13
  181. package/dist/vue-components.es55.js +0 -67
  182. package/dist/vue-components.es56.js +0 -58
  183. package/dist/vue-components.es57.js +0 -19
  184. package/dist/vue-components.es58.js +0 -35
  185. package/dist/vue-components.es59.js +0 -31
  186. package/dist/vue-components.es6.js +0 -69
  187. package/dist/vue-components.es60.js +0 -44
  188. package/dist/vue-components.es61.js +0 -4
  189. package/dist/vue-components.es62.js +0 -46
  190. package/dist/vue-components.es63.js +0 -4
  191. package/dist/vue-components.es7.js +0 -83
  192. package/dist/vue-components.es8.js +0 -63
  193. package/dist/vue-components.es9.js +0 -21
  194. package/src/components/OmegaForm/OmegaFormStuff.ts +0 -1184
  195. package/src/constants/index.ts +0 -1
package/package.json CHANGED
@@ -1,39 +1,39 @@
1
1
  {
2
2
  "name": "@effect-app/vue-components",
3
- "version": "4.0.0-beta.18",
3
+ "version": "4.0.0-beta.180",
4
4
  "peerDependencies": {
5
5
  "@mdi/js": "^7.4.47",
6
- "effect": "^4.0.0-beta.31",
7
- "intl-messageformat": "^11.1.2",
6
+ "effect": "^4.0.0-beta.59",
7
+ "intl-messageformat": "^11.2.2",
8
8
  "mdi-js": "^1.0.1",
9
9
  "primeflex": "^4.0.0",
10
10
  "primeicons": "^7.0.0",
11
- "primevue": "^4.5.4",
12
- "vue": "^3.5.29",
13
- "vuetify": "^4.0.1"
11
+ "primevue": "^4.5.5",
12
+ "vue": "^3.5.33",
13
+ "vuetify": "^4.0.6"
14
14
  },
15
15
  "devDependencies": {
16
- "@storybook/vue3": "^10.2.15",
17
- "@storybook/vue3-vite": "^10.2.15",
18
- "@types/node": "^25.3.3",
19
- "@vitejs/plugin-vue": "^6.0.4",
20
- "@vue/test-utils": "^2.4.6",
16
+ "@storybook/vue3": "^10.3.5",
17
+ "@storybook/vue3-vite": "^10.3.5",
18
+ "@types/node": "^25.6.0",
19
+ "@vitejs/plugin-vue": "^6.0.6",
20
+ "@vue/test-utils": "^2.4.9",
21
21
  "@vueuse/core": "^14.2.1",
22
- "dprint": "^0.52.0",
23
- "jsdom": "^28.1.0",
22
+ "jsdom": "^29.1.0",
24
23
  "rimraf": "^6.1.3",
25
- "sass": "^1.97.3",
26
- "storybook": "^10.2.15",
27
- "typescript": "~5.9.3",
28
- "vite": "^7.3.1",
29
- "vite-plugin-css-injected-by-js": "^4.0.1",
24
+ "sass": "^1.99.0",
25
+ "storybook": "^10.3.5",
26
+ "storybook-vue3-router": "^7.0.0",
27
+ "typescript": "~6.0.3",
28
+ "vite": "^8.0.10",
29
+ "vite-plugin-css-injected-by-js": "^5.0.1",
30
30
  "vitepress": "^1.6.4",
31
- "vitest": "^4.0.18",
32
- "vue-router": "^5.0.3",
31
+ "vitest": "^4.1.5",
32
+ "vue-router": "^5.0.6",
33
33
  "vue-toastification": "^2.0.0-rc.5",
34
- "vue-tsc": "^3.2.5",
35
- "vuetify": "^4.0.1",
36
- "@effect-app/eslint-shared-config": "0.5.7-beta.2"
34
+ "vue-tsc": "^3.2.7",
35
+ "vuetify": "^4.0.6",
36
+ "@effect-app/eslint-shared-config": "0.6.0-beta.18"
37
37
  },
38
38
  "files": [
39
39
  "src",
@@ -45,28 +45,32 @@
45
45
  "types": "./dist/types/index.d.ts",
46
46
  "import": "./dist/vue-components.es.js"
47
47
  },
48
- "./dist/vue-components.css": "./dist/vue-components.css"
48
+ "./dist/vue-components.css": "./dist/vue-components.css",
49
+ "./reset.css": {
50
+ "development": "./src/reset.css",
51
+ "default": "./dist/reset.css"
52
+ }
49
53
  },
50
54
  "dependencies": {
51
- "@opentelemetry/api": "^1.9.0",
55
+ "@opentelemetry/api": "^1.9.1",
52
56
  "@tanstack/vue-form": "^1.23.5",
53
57
  "highlight.js": "^11.11.1",
54
58
  "mitt": "^3.0.1",
55
59
  "vue3-highlightjs": "^1.0.5",
56
- "@effect-app/vue": "4.0.0-beta.18",
57
- "effect-app": "4.0.0-beta.18"
60
+ "@effect-app/vue": "4.0.0-beta.180",
61
+ "effect-app": "4.0.0-beta.180"
58
62
  },
59
63
  "scripts": {
60
64
  "check": "vue-tsc",
61
65
  "build": "pnpm build:run",
62
- "build:run": "rimraf dist && vue-tsc && vite build",
66
+ "build:run": "rimraf dist && vue-tsc && vite build && cp src/reset.css dist/reset.css",
63
67
  "docs:dev": "vitepress dev docs",
64
68
  "docs:build": "vitepress build docs",
65
69
  "docs:serve": "vitepress serve docs",
66
- "lint": "NODE_OPTIONS=--max-old-space-size=8192 eslint src stories .storybook",
70
+ "lint": "oxlint src stories && NODE_OPTIONS=--max-old-space-size=8192 eslint src stories .storybook && pnpm exec dprint check --config ../../dprint.jsonc .",
67
71
  "ncu": "ncu",
68
72
  "clean": "rm -rf dist",
69
- "lint-fix": "pnpm lint --fix",
73
+ "lint-fix": "oxlint --fix src stories && NODE_OPTIONS=--max-old-space-size=8192 eslint --fix src stories .storybook && pnpm exec dprint fmt --config ../../dprint.jsonc .",
70
74
  "storybook": "storybook dev -p 6006",
71
75
  "build-storybook": "storybook build",
72
76
  "test": "vitest",
@@ -41,7 +41,9 @@ const handleClick = () => {
41
41
  const input = ("input" in props && props.input
42
42
  ? props.input
43
43
  : undefined) as unknown as I
44
- ;(props.command.handle as any)(input)
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- command.handle has a generic signature mismatched by erased input type
45
+ const handle = props.command.handle as any
46
+ handle(input)
45
47
  }
46
48
  </script>
47
49
  <script lang="ts">
@@ -66,7 +68,7 @@ export default {
66
68
  :label="command.label"
67
69
  :title="title ?? command.action"
68
70
  >
69
- <span>{{ command.label }}</span>
71
+ {{ command.label }}
70
72
  </slot>
71
73
  </v-btn>
72
74
  <v-btn
@@ -12,9 +12,7 @@
12
12
  :is="form.Field"
13
13
  v-for="(_, i) of items"
14
14
  :key="`${name}[${Number(i)}]`"
15
- :name="// eslint-disable-next-line
16
- `${name}[${Number(i)}]` as DeepKeys<From>
17
- "
15
+ :name="`${name}[${Number(i)}]` as DeepKeys<From>"
18
16
  >
19
17
  <template #default="{ field: subField, state: subState }">
20
18
  <slot
@@ -42,11 +40,15 @@
42
40
  <script
43
41
  setup
44
42
  lang="ts"
45
- generic="From extends Record<PropertyKey, any>, To extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
43
+ generic="
44
+ From extends Record<PropertyKey, any>,
45
+ To extends Record<PropertyKey, any>,
46
+ Name extends DeepKeys<From>
47
+ "
46
48
  >
47
49
  import { type DeepKeys } from "@tanstack/vue-form"
48
50
  import { computed, onMounted, provide } from "vue"
49
- import { type OmegaArrayProps } from "./OmegaFormStuff"
51
+ import { type OmegaArrayProps } from "./types"
50
52
 
51
53
  const props = defineProps<OmegaArrayProps<From, To, Name>>()
52
54
 
@@ -14,15 +14,17 @@
14
14
  <script
15
15
  setup
16
16
  lang="ts"
17
- generic="// dprint-ignore
17
+ generic="
18
18
  From extends Record<PropertyKey, string>,
19
19
  To extends Record<PropertyKey, string>,
20
- Name extends DeepKeys<From>"
20
+ Name extends DeepKeys<From>
21
+ "
21
22
  >
22
23
  import { type DeepKeys } from "@tanstack/vue-form"
23
24
  import { Order } from "effect-app"
24
25
  import { computed } from "vue"
25
- import { type FieldMeta, type FieldPath, type OmegaAutoGenMeta, type OmegaInputProps } from "./OmegaFormStuff"
26
+ import { type FieldMeta } from "./meta/types"
27
+ import { type FieldPath, type OmegaAutoGenMeta, type OmegaInputProps } from "./types"
26
28
 
27
29
  type NewMeta = OmegaAutoGenMeta<From, To, Name>
28
30
 
@@ -102,7 +102,7 @@
102
102
  import type { StandardSchemaV1Issue } from "@tanstack/vue-form"
103
103
  import { computed, getCurrentInstance } from "vue"
104
104
  import { useIntl } from "../../utils"
105
- import { type OmegaError } from "./OmegaFormStuff"
105
+ import { type OmegaError } from "./types"
106
106
 
107
107
  const instance = getCurrentInstance()
108
108
  const vuetified = instance?.appContext.components["VAlert"]
@@ -12,13 +12,17 @@
12
12
  <script
13
13
  setup
14
14
  lang="ts"
15
- generic="From extends Record<PropertyKey, any>, To extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
15
+ generic="
16
+ From extends Record<PropertyKey, any>,
17
+ To extends Record<PropertyKey, any>,
18
+ Name extends DeepKeys<From>
19
+ "
16
20
  >
17
21
  import { type DeepKeys } from "@tanstack/vue-form"
18
22
  import { inject } from "vue"
19
23
  import type { MergedInputProps } from "./InputProps"
20
- import type { BaseProps, DefaultTypeProps, OmegaInputProps } from "./OmegaFormStuff"
21
24
  import OmegaInput from "./OmegaInput.vue"
25
+ import type { BaseProps, DefaultTypeProps, OmegaInputProps } from "./types"
22
26
  import { OmegaFormKey } from "./useOmegaForm"
23
27
 
24
28
  const form = inject(OmegaFormKey) as unknown as OmegaInputProps<
@@ -1,18 +1,14 @@
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
  <template #default="{ field, state }">
12
8
  <OmegaInternalInput
13
9
  v-if="meta"
14
10
  v-bind="{ ...$attrs, ...$props, inputClass: computedClass }"
15
- :field="field"
11
+ :field="field as any"
16
12
  :state="state"
17
13
  :register="form.registerField"
18
14
  :label="label ?? errori18n(propsName)"
@@ -38,14 +34,19 @@
38
34
  <script
39
35
  setup
40
36
  lang="ts"
41
- generic="From extends Record<PropertyKey, any>, To extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
37
+ generic="
38
+ From extends Record<PropertyKey, any>,
39
+ To extends Record<PropertyKey, any>,
40
+ Name extends DeepKeys<From>
41
+ "
42
42
  >
43
+ /* eslint-disable @typescript-eslint/no-explicit-any -- TanStack Form Field generic interop and slot prop typing */
43
44
  import { type DeepKeys } from "@tanstack/vue-form"
44
45
  import { computed, inject, type Ref, useAttrs } from "vue"
45
- import { useIntl } from "../../utils"
46
- import { type FieldMeta, generateInputStandardSchemaFromFieldMeta, type OmegaInputPropsBase } from "./OmegaFormStuff"
46
+ import { useErrorLabel } from "./errors"
47
+ import { type FieldMeta } from "./meta/types"
47
48
  import OmegaInternalInput from "./OmegaInternalInput.vue"
48
- import { useErrorLabel } from "./useOmegaForm"
49
+ import { type OmegaInputPropsBase } from "./types"
49
50
 
50
51
  const props = defineProps<OmegaInputPropsBase<From, To, Name>>()
51
52
 
@@ -57,13 +58,10 @@ defineSlots<{
57
58
  default?: (props: any) => any
58
59
  }>()
59
60
 
60
- defineOptions({
61
- inheritAttrs: false
62
- })
61
+ defineOptions({ inheritAttrs: false })
63
62
 
64
63
  const attrs = useAttrs()
65
64
 
66
- // Compute the class to use based on inputClass prop
67
65
  const computedClass = computed(() => {
68
66
  if (props.inputClass === null) return undefined
69
67
  if (props.inputClass !== undefined) return props.inputClass
@@ -82,28 +80,5 @@ const meta = computed(() => {
82
80
  return props.form.meta[propsName.value]
83
81
  })
84
82
 
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)
106
- })
107
-
108
83
  const errori18n = useErrorLabel(props.form)
109
84
  </script>
@@ -1,7 +1,10 @@
1
1
  <template>
2
2
  <div
3
3
  class="omega-input"
4
- @focusout="$emit('blur', $event)"
4
+ @focusout="(e) => {
5
+ $emit('blur', e)
6
+ field.handleBlur()
7
+ }"
5
8
  @focusin="$emit('focus', $event)"
6
9
  >
7
10
  <component
@@ -204,8 +207,8 @@
204
207
  generic="From extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
205
208
  >
206
209
  import { type DeepKeys } from "@tanstack/vue-form"
207
- import { getInputType } from "../OmegaForm/OmegaFormStuff"
208
210
  import type { VuetifyInputProps } from "./InputProps"
211
+ import { getInputType } from "./inputs"
209
212
 
210
213
  defineProps<VuetifyInputProps<From, Name>>()
211
214
 
@@ -24,11 +24,13 @@
24
24
  lang="ts"
25
25
  generic="From extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
26
26
  >
27
+ /* eslint-disable @typescript-eslint/no-explicit-any -- TanStack Form / Vue attrs interop */
27
28
  import { type DeepKeys, useStore } from "@tanstack/vue-form"
28
29
  import { computed, type ComputedRef, getCurrentInstance, useAttrs, useId, useSlots } from "vue"
29
30
  import type { InputProps, OmegaFieldInternalApi } from "./InputProps"
30
- import type { FieldValidators, MetaRecord, NestedKeyOf, TypeOverride } from "./OmegaFormStuff"
31
+ import type { MetaRecord, NestedKeyOf } from "./meta/types"
31
32
  import OmegaInputVuetify from "./OmegaInputVuetify.vue"
33
+ import type { FieldValidators, TypeOverride } from "./types"
32
34
 
33
35
  defineOptions({
34
36
  inheritAttrs: false
@@ -84,7 +86,8 @@ const id = useId()
84
86
 
85
87
  const fieldApi = props.field
86
88
 
87
- const fieldState = useStore(fieldApi.store, (state) => state)
89
+ // Subscribed for side-effect: keeps component reactive to fieldApi.store changes
90
+ const _fieldState = useStore(fieldApi.store, (state) => state)
88
91
 
89
92
  // Get errors from form-level fieldMeta (persists across Field re-mounts)
90
93
  const formFieldMeta = useStore(fieldApi.form.store, (state) => state.fieldMeta)
@@ -128,6 +131,13 @@ const handleChange: OmegaFieldInternalApi<From, Name>["handleChange"] = (value)
128
131
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
132
  : null as any
130
133
  )
134
+ } else if (props.meta?.isOptionalKey) {
135
+ // `S.optionalKey` expects the key to be ABSENT from the submitted
136
+ // object, not present-with-undefined. Remove it from form state
137
+ // rather than setting it to `undefined`. Note: this is distinct
138
+ // from `required: false`, which may also just mean "empty string
139
+ // is valid" for unconstrained `S.String` fields.
140
+ props.field.form.deleteField(props.field.name)
131
141
  } else {
132
142
  // Keep the actual value (e.g., empty string for S.String fields)
133
143
  props.field.handleChange(value)
@@ -135,10 +145,6 @@ const handleChange: OmegaFieldInternalApi<From, Name>["handleChange"] = (value)
135
145
  } else {
136
146
  props.field.handleChange(value)
137
147
  }
138
-
139
- // whenever we change the field, regardless if we set it to null, we should reset onSubmit.
140
- // not sure why this is not the case in tanstack form.
141
- props.field.setMeta((m) => ({ ...m, errorMap: { ...m.errorMap, onSubmit: undefined } }))
142
148
  }
143
149
 
144
150
  // Note: Default value normalization (converting empty strings to null/undefined for nullable fields)
@@ -165,11 +171,13 @@ const inputProps: ComputedRef<InputProps<From, Name>> = computed(() => ({
165
171
  minLength: props.meta?.type === "string" && props.meta?.minLength,
166
172
  maxLength: props.meta?.type === "string" && props.meta?.maxLength,
167
173
  max: (props.meta?.type === "number")
168
- && (props.meta?.maximum
169
- ?? (typeof props.meta?.exclusiveMaximum === "number" && props.meta.exclusiveMaximum - 1)),
174
+ ? (props.meta?.maximum
175
+ ?? (typeof props.meta?.exclusiveMaximum === "number" ? props.meta.exclusiveMaximum - 1 : undefined))
176
+ : undefined,
170
177
  min: (props.meta?.type === "number")
171
- && (props.meta?.minimum
172
- ?? (typeof props.meta?.exclusiveMinimum === "number" && props.meta.exclusiveMinimum + 1)),
178
+ ? (props.meta?.minimum
179
+ ?? (typeof props.meta?.exclusiveMinimum === "number" ? props.meta.exclusiveMinimum + 1 : undefined))
180
+ : undefined,
173
181
  errorMessages: errors.value,
174
182
  error: !!errors.value.length,
175
183
  type: fieldType.value,
@@ -1,15 +1,17 @@
1
1
  <script
2
2
  setup
3
3
  lang="ts"
4
- generic="From extends Record<PropertyKey, any>,
4
+ generic="
5
+ From extends Record<PropertyKey, any>,
5
6
  To extends Record<PropertyKey, any>,
6
- Name extends DeepKeys<From> | undefined = DeepKeys<From>"
7
+ Name extends DeepKeys<From> | undefined = DeepKeys<From>
8
+ "
7
9
  >
8
10
  import { type DeepKeys } from "@tanstack/vue-form"
9
11
  import { computed, provide, ref, watch } from "vue"
10
12
  import { type TaggedUnionOption } from "./InputProps"
11
- import { type FieldPath } from "./OmegaFormStuff"
12
13
  import OmegaTaggedUnionInternal from "./OmegaTaggedUnionInternal.vue"
14
+ import { type FieldPath } from "./types"
13
15
  import { type useOmegaForm } from "./useOmegaForm"
14
16
 
15
17
  const props = defineProps<{
@@ -30,6 +32,7 @@ watch(
30
32
  () => {
31
33
  const path = tagPath.value
32
34
  // Navigate to the nested value
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- traversing arbitrary nested values
33
36
  return path.split(".").reduce((acc: any, key) => acc?.[key], formValues.value) as string | null
34
37
  },
35
38
  (newTag) => {
@@ -9,7 +9,11 @@
9
9
  <script
10
10
  setup
11
11
  lang="ts"
12
- generic="From extends Record<PropertyKey, any>, To extends Record<PropertyKey, any>, Name extends DeepKeys<From>"
12
+ generic="
13
+ From extends Record<PropertyKey, any>,
14
+ To extends Record<PropertyKey, any>,
15
+ Name extends DeepKeys<From>
16
+ "
13
17
  >
14
18
  import { type DeepKeys, type DeepValue } from "@tanstack/vue-form"
15
19
  import { watch } from "vue"
@@ -12,10 +12,12 @@
12
12
  <script
13
13
  setup
14
14
  lang="ts"
15
- generic="From extends Record<PropertyKey, any>,
15
+ generic="
16
+ From extends Record<PropertyKey, any>,
16
17
  To extends Record<PropertyKey, any>,
17
18
  K extends keyof OmegaFormState<From, To> = keyof OmegaFormState<From, To>,
18
- Props = DefaultTypeProps"
19
+ Props = DefaultTypeProps
20
+ "
19
21
  >
20
22
  /**
21
23
  * Form component that wraps TanStack Form's useForm hook
@@ -30,7 +32,7 @@
30
32
  import { useStore } from "@tanstack/vue-form"
31
33
  import { usePreventClose } from "./blockDialog"
32
34
  import { getOmegaStore } from "./getOmegaStore"
33
- import { type DefaultTypeProps, type OmegaFormApi, type OmegaFormState } from "./OmegaFormStuff"
35
+ import { type DefaultTypeProps, type OmegaFormApi, type OmegaFormState } from "./types"
34
36
  import { type OmegaFormReturn } from "./useOmegaForm"
35
37
 
36
38
  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
- if (!confirm("Es sind ungespeicherte Änderungen vorhanden. Wirklich schließen?")) {
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" as any) ?? defaultMessage
35
+ if (!confirm(message)) {
27
36
  evt.prevent = true
28
37
  }
29
38
  }
@@ -1,8 +1,9 @@
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 DefaultTypeProps } from "./types"
6
7
  import { useOmegaForm } from "./useOmegaForm"
7
8
 
8
9
  export const createUseFormWithCustomInput = <
@@ -0,0 +1,136 @@
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
+ export const useErrorLabel = (form: OF<any, any>) => {
9
+ const { formatMessage } = useIntl()
10
+ const humanize = (str: string) => {
11
+ return str
12
+ .replace(/([A-Z])/g, " $1") // Add space before capital letters
13
+ .replace(/^./, (char) => char.toUpperCase()) // Capitalize the first letter
14
+ .trim() // Remove leading/trailing spaces
15
+ }
16
+ const fallback = (propsName: string) =>
17
+ formatMessage
18
+ ? formatMessage({ id: `general.fields.${propsName}`, defaultMessage: humanize(propsName) })
19
+ : humanize(propsName)
20
+ const i18n = (propsName: string) =>
21
+ form.i18nNamespace
22
+ ? formatMessage({ id: `${form.i18nNamespace}.fields.${propsName}`, defaultMessage: fallback(propsName) })
23
+ : fallback(propsName)
24
+
25
+ return i18n
26
+ }
27
+
28
+ export const eHoc = (errorProps: {
29
+ form: OF<any, any>
30
+ fieldMap: Ref<Map<string, { id: string; label: string }>>
31
+ }) => {
32
+ return function FormHoc<P>(
33
+ WrappedComponent: Component<P>
34
+ ): ConcreteComponent<P> {
35
+ return {
36
+ setup() {
37
+ const { fieldMap, form } = errorProps
38
+ const generalErrors = form.useStore((state) => state.errors)
39
+ const fieldMeta = form.useStore((state) => state.fieldMeta)
40
+ const errorMap = form.useStore((state) => state.errorMap)
41
+
42
+ const errorLabel = useErrorLabel(form)
43
+
44
+ const errors = computed(() => {
45
+ // Collect errors from fieldMeta (field-level errors for registered fields)
46
+ const fieldErrors = Object.entries(fieldMeta.value).reduce<OmegaError[]>((acc, [key, m]) => {
47
+ const fieldErrors = (m as { errors?: Array<{ message?: string }> } | undefined)?.errors ?? []
48
+ if (!fieldErrors.length) {
49
+ return acc
50
+ }
51
+
52
+ const fieldInfo = fieldMap.value.get(key)
53
+ if (!fieldInfo) {
54
+ return acc
55
+ }
56
+
57
+ acc.push({
58
+ label: fieldInfo.label,
59
+ inputId: fieldInfo.id,
60
+ errors: [fieldErrors[0]?.message].filter(Boolean) as string[]
61
+ })
62
+
63
+ return acc
64
+ }, [])
65
+
66
+ // Collect errors from errorMap.onDynamic / errorMap.onSubmit ONLY for fields that are NOT registered
67
+ // (registered fields already have their errors in fieldMeta).
68
+ // Our localized standard schema writes to onDynamic via validationLogic: revalidateLogic();
69
+ // caller-provided validators.onSubmit (via tanstackFormOptions spread) writes to onSubmit.
70
+ const submitErrors: OmegaError[] = []
71
+ const submitIssueMaps = [errorMap.value.onDynamic, errorMap.value.onSubmit].filter(
72
+ Boolean
73
+ ) as unknown as Array<
74
+ Record<string, unknown>
75
+ >
76
+ const seenPaths = new Set<string>()
77
+ for (const issuesByPath of submitIssueMaps) {
78
+ for (const [_, issues] of Object.entries(issuesByPath)) {
79
+ if (Array.isArray(issues) && issues.length) {
80
+ for (const issue of issues) {
81
+ const issAny: any = issue
82
+ if (issAny?.path && Array.isArray(issAny.path) && issAny.path.length) {
83
+ const fieldPath = issAny.path.join(".")
84
+ if (!fieldMap.value.has(fieldPath) && !seenPaths.has(fieldPath)) {
85
+ seenPaths.add(fieldPath)
86
+ submitErrors.push({
87
+ label: errorLabel(fieldPath),
88
+ inputId: fieldPath,
89
+ errors: [issAny.message].filter(Boolean)
90
+ })
91
+ break
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ // Combine both error sources (no need to check for duplicates since they're mutually exclusive)
100
+ return [...fieldErrors, ...submitErrors]
101
+ })
102
+
103
+ return {
104
+ generalErrors,
105
+ errors
106
+ }
107
+ },
108
+ render({ errors, generalErrors }: any) {
109
+ return h(WrappedComponent, {
110
+ errors,
111
+ generalErrors,
112
+ ...this.$attrs
113
+ } as any, this.$slots)
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ export const makeFieldMap = () => {
120
+ const fieldMap = ref(new Map<string, { label: string; id: string }>())
121
+ const registerField = (field: ComputedRef<{ name: string; label: string; id: string }>) => {
122
+ watch(field, (f) => {
123
+ fieldMap.value.set(f.name, { label: f.label, id: f.id })
124
+ }, { immediate: true })
125
+ onUnmounted(() => {
126
+ // Only delete if we still own this entry (id matches)
127
+ // This prevents old components from deleting entries registered by new components
128
+ // during re-mount transitions (e.g., when :key changes)
129
+ const currentEntry = fieldMap.value.get(field.value.name)
130
+ if (currentEntry?.id === field.value.id) {
131
+ fieldMap.value.delete(field.value.name)
132
+ }
133
+ })
134
+ }
135
+ return { fieldMap, registerField }
136
+ }
@@ -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
+ } as any, this.$slots)
16
+ }
17
+ }
18
+ }
19
+ }