@effect-app/vue-components 2.10.11 → 2.11.0

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.
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <component
3
3
  :is="form.Field"
4
+ :key="fieldKey"
4
5
  :name="name"
5
6
  :validators="{
6
7
  onChange: schema,
@@ -41,6 +42,7 @@
41
42
  >
42
43
  import { type DeepKeys } from "@tanstack/vue-form"
43
44
  import { computed, inject, type Ref, useAttrs } from "vue"
45
+ import { useIntl } from "../../utils"
44
46
  import { type FieldMeta, generateInputStandardSchemaFromFieldMeta, type OmegaInputPropsBase } from "./OmegaFormStuff"
45
47
  import OmegaInternalInput from "./OmegaInternalInput.vue"
46
48
  import { useErrorLabel } from "./useOmegaForm"
@@ -74,18 +76,35 @@ const getMetaFromArray = inject<Ref<(name: string) => FieldMeta | null> | null>(
74
76
  )
75
77
 
76
78
  const meta = computed(() => {
77
- if (getMetaFromArray?.value && getMetaFromArray.value(props.name as DeepKeys<From>)) {
78
- return getMetaFromArray.value(propsName.value)
79
+ const fromArray = getMetaFromArray?.value?.(props.name as DeepKeys<From>)
80
+ if (fromArray) {
81
+ return fromArray
79
82
  }
80
- return props.form.meta[propsName.value]
83
+ const formMeta = props.form.meta[propsName.value]
84
+ return formMeta
81
85
  })
82
86
 
87
+ // Key to force Field re-mount when meta type changes (for TaggedUnion support)
88
+ const fieldKey = computed(() => {
89
+ const m = meta.value
90
+ if (!m) return propsName.value
91
+ // Include type and key constraints in the key so Field re-mounts when validation rules change
92
+ // Cast to any since not all FieldMeta variants have these properties
93
+ const fm = m as any
94
+ return `${propsName.value}-${fm.type}-${fm.minLength ?? ""}-${fm.maxLength ?? ""}-${fm.minimum ?? ""}-${
95
+ fm.maximum ?? ""
96
+ }`
97
+ })
98
+
99
+ // Call useIntl during setup to avoid issues when computed re-evaluates
100
+ const { trans } = useIntl()
101
+
83
102
  const schema = computed(() => {
84
103
  if (!meta.value) {
85
104
  console.log(props.name, Object.keys(props.form.meta), props.form.meta)
86
105
  throw new Error("Meta is undefined")
87
106
  }
88
- return generateInputStandardSchemaFromFieldMeta(meta.value)
107
+ return generateInputStandardSchemaFromFieldMeta(meta.value, trans)
89
108
  })
90
109
 
91
110
  const errori18n = useErrorLabel(props.form)
@@ -6,18 +6,57 @@
6
6
  Name extends DeepKeys<From> | undefined = DeepKeys<From>"
7
7
  >
8
8
  import { type DeepKeys } from "@tanstack/vue-form"
9
+ import { computed, provide, ref, watch } from "vue"
9
10
  import { type TaggedUnionOption } from "./InputProps"
10
11
  import { type FieldPath } from "./OmegaFormStuff"
11
12
  import OmegaTaggedUnionInternal from "./OmegaTaggedUnionInternal.vue"
12
13
  import { type useOmegaForm } from "./useOmegaForm"
13
14
 
14
- defineProps<{
15
+ const props = defineProps<{
15
16
  name?: Name
16
17
  form: ReturnType<typeof useOmegaForm<From, To>>
17
18
  type?: "select" | "radio"
18
19
  options: TaggedUnionOption<From, Name>[]
19
20
  label?: string
20
21
  }>()
22
+
23
+ // Track the current tag value reactively
24
+ const currentTag = ref<string | null>(null)
25
+
26
+ // Watch the form's _tag field value
27
+ const tagPath = computed(() => props.name ? `${props.name}._tag` : "_tag")
28
+ const formValues = props.form.useStore((state) => state.values)
29
+ watch(
30
+ () => {
31
+ const path = tagPath.value
32
+ // Navigate to the nested value
33
+ return path.split(".").reduce((acc: any, key) => acc?.[key], formValues.value) as string | null
34
+ },
35
+ (newTag) => {
36
+ currentTag.value = newTag ?? null
37
+ },
38
+ { immediate: true }
39
+ )
40
+
41
+ // Provide tag-specific metadata to all child Input components
42
+ const getMetaFromArray = computed(() => {
43
+ const tag = currentTag.value
44
+
45
+ const getMeta = (path: string) => {
46
+ if (!tag) return null
47
+
48
+ // Get the tag-specific metadata
49
+ const tagMeta = props.form.unionMeta[tag]
50
+ if (!tagMeta) return null
51
+
52
+ // Look up the meta for this path
53
+ return tagMeta[path as keyof typeof tagMeta] ?? null
54
+ }
55
+
56
+ return getMeta
57
+ })
58
+
59
+ provide("getMetaFromArray", getMetaFromArray)
21
60
  </script>
22
61
 
23
62
  <template>
@@ -31,8 +31,15 @@ watch(() => props.state, (newTag, oldTag) => {
31
31
  props.field.setValue(null as DeepValue<From, Name>)
32
32
  }
33
33
 
34
- if (newTag !== oldTag) {
35
- props.form.reset(values.value)
34
+ if (newTag !== oldTag && newTag) {
35
+ // Get default values for the new tag to ensure correct types
36
+ const tagDefaults = (props.form as any).unionDefaultValues?.[newTag as string] ?? {}
37
+ // Merge: keep _tag from current values, but use tag defaults for other fields
38
+ const resetValues = {
39
+ ...tagDefaults,
40
+ _tag: newTag
41
+ }
42
+ props.form.reset(resetValues as any)
36
43
  setTimeout(() => {
37
44
  props.field.validate("change")
38
45
  }, 0)
@@ -217,6 +217,8 @@ export type OmegaConfig<T> = {
217
217
 
218
218
  export interface OF<From, To> extends OmegaFormApi<From, To> {
219
219
  meta: MetaRecord<From>
220
+ unionMeta: Record<string, MetaRecord<From>>
221
+ unionDefaultValues: Record<string, Record<string, any>>
220
222
  clear: () => void
221
223
  i18nNamespace?: string
222
224
  ignorePreventCloseEvents?: boolean
@@ -682,7 +684,7 @@ export const useOmegaForm = <
682
684
  const standardSchema = S.standardSchemaV1(schema)
683
685
  const decode = S.decode(schema)
684
686
 
685
- const { meta } = generateMetaFromSchema(schema)
687
+ const { meta, unionDefaultValues, unionMeta } = generateMetaFromSchema(schema)
686
688
 
687
689
  const persistencyKey = computed(() => {
688
690
  if (omegaConfig?.persistency?.id) {
@@ -952,6 +954,8 @@ export const useOmegaForm = <
952
954
  i18nNamespace: omegaConfig?.i18nNamespace,
953
955
  ignorePreventCloseEvents: omegaConfig?.ignorePreventCloseEvents,
954
956
  meta,
957
+ unionMeta,
958
+ unionDefaultValues,
955
959
  clear,
956
960
  handleSubmit: (meta?: Record<string, any>) => {
957
961
  const span = api.trace.getSpan(api.context.active())
@@ -961,7 +965,15 @@ export const useOmegaForm = <
961
965
  handleSubmitEffect,
962
966
  registerField: (field: ComputedRef<{ name: string; label: string; id: string }>) => {
963
967
  watch(field, (f) => fieldMap.value.set(f.name, { label: f.label, id: f.id }), { immediate: true })
964
- onUnmounted(() => fieldMap.value.delete(field.value.name)) // todo; perhap only when owned (id match)
968
+ onUnmounted(() => {
969
+ // Only delete if this component instance still owns the registration (id matches)
970
+ // This prevents the old component from removing the new component's registration
971
+ // when Vue re-keys and mounts new before unmounting old
972
+ const current = fieldMap.value.get(field.value.name)
973
+ if (current?.id === field.value.id) {
974
+ fieldMap.value.delete(field.value.name)
975
+ }
976
+ })
965
977
  }
966
978
  })
967
979
 
@@ -1,4 +0,0 @@
1
- import f from "./vue-components.es19.js";
2
- export {
3
- f as default
4
- };
@@ -1,9 +0,0 @@
1
- const s = (t, e) => {
2
- const o = t.__vccOpts || t;
3
- for (const [r, c] of e)
4
- o[r] = c;
5
- return o;
6
- };
7
- export {
8
- s as default
9
- };