@effect-app/vue-components 0.27.16 → 1.0.0-next.1

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 (74) hide show
  1. package/dist/types/components/OmegaForm/OmegaArray.vue.d.ts +2 -3
  2. package/dist/types/components/OmegaForm/OmegaErrorsContext.d.ts +4 -4
  3. package/dist/types/components/OmegaForm/OmegaErrorsInternal.vue.d.ts +22 -0
  4. package/dist/types/components/OmegaForm/OmegaInput.vue.d.ts +1 -2
  5. package/dist/types/components/OmegaForm/OmegaWrapper.vue.d.ts +7 -37
  6. package/dist/types/components/OmegaForm/index.d.ts +1 -7
  7. package/dist/types/components/OmegaForm/useOmegaForm.d.ts +65 -1
  8. package/dist/types/utils/index.d.ts +9 -0
  9. package/dist/vue-components.es.js +16 -25
  10. package/dist/vue-components.es10.js +5 -333
  11. package/dist/vue-components.es11.js +54 -6
  12. package/dist/vue-components.es12.js +71 -6
  13. package/dist/vue-components.es13.js +6 -2
  14. package/dist/vue-components.es14.js +6 -2
  15. package/dist/vue-components.es15.js +3 -86
  16. package/dist/vue-components.es16.js +5 -0
  17. package/dist/vue-components.es17.js +89 -7
  18. package/dist/vue-components.es18.js +42 -2
  19. package/dist/vue-components.es19.js +1 -1
  20. package/dist/vue-components.es2.js +10 -20
  21. package/dist/vue-components.es20.js +2 -126
  22. package/dist/vue-components.es21.js +92 -0
  23. package/dist/vue-components.es23.js +7 -5
  24. package/dist/vue-components.es24.js +31 -3
  25. package/dist/vue-components.es26.js +2 -11
  26. package/dist/vue-components.es27.js +23 -90
  27. package/dist/vue-components.es28.js +5 -42
  28. package/dist/vue-components.es29.js +21 -2
  29. package/dist/vue-components.es3.js +12 -8
  30. package/dist/vue-components.es30.js +27 -20
  31. package/dist/vue-components.es31.js +22 -5
  32. package/dist/vue-components.es32.js +6 -15
  33. package/dist/vue-components.es33.js +5 -30
  34. package/dist/vue-components.es34.js +19 -22
  35. package/dist/vue-components.es35.js +9 -12
  36. package/dist/vue-components.es36.js +31 -5
  37. package/dist/vue-components.es37.js +48 -19
  38. package/dist/vue-components.es38.js +26 -9
  39. package/dist/vue-components.es39.js +11 -31
  40. package/dist/vue-components.es4.js +47 -47
  41. package/dist/vue-components.es40.js +114 -47
  42. package/dist/vue-components.es42.js +1 -1
  43. package/dist/vue-components.es44.js +1 -1
  44. package/dist/vue-components.es45.js +3 -3
  45. package/dist/vue-components.es46.js +3 -3
  46. package/dist/vue-components.es47.js +1 -1
  47. package/dist/vue-components.es48.js +4 -115
  48. package/dist/vue-components.es49.js +4 -0
  49. package/dist/vue-components.es5.js +29 -70
  50. package/dist/vue-components.es50.js +44 -2
  51. package/dist/vue-components.es51.js +2 -44
  52. package/dist/vue-components.es6.js +209 -31
  53. package/dist/vue-components.es7.js +331 -49
  54. package/dist/vue-components.es8.js +2 -29
  55. package/dist/vue-components.es9.js +10 -148
  56. package/package.json +2 -2
  57. package/src/components/OmegaForm/OmegaArray.vue +4 -2
  58. package/src/components/OmegaForm/OmegaAutoGen.vue +5 -5
  59. package/src/components/OmegaForm/OmegaErrorsContext.ts +10 -8
  60. package/src/components/OmegaForm/{OmegaErrors.vue → OmegaErrorsInternal.vue} +11 -5
  61. package/src/components/OmegaForm/OmegaFormInput.vue +4 -2
  62. package/src/components/OmegaForm/OmegaFormStuff.ts +5 -6
  63. package/src/components/OmegaForm/OmegaInput.vue +6 -5
  64. package/src/components/OmegaForm/OmegaInternalInput.vue +1 -2
  65. package/src/components/OmegaForm/OmegaWrapper.vue +23 -247
  66. package/src/components/OmegaForm/index.ts +1 -7
  67. package/src/components/OmegaForm/useOmegaForm.ts +239 -7
  68. package/src/utils/index.ts +16 -0
  69. package/dist/types/components/OmegaForm/OmegaErrors.vue.d.ts +0 -15
  70. package/dist/vue-components.es22.js +0 -12
  71. package/dist/vue-components.es25.js +0 -5
  72. package/dist/vue-components.es41.js +0 -28
  73. package/dist/vue-components.es52.js +0 -4
  74. package/dist/vue-components.es53.js +0 -6
@@ -76,20 +76,26 @@ import { mdiLink } from "@mdi/js"
76
76
  import type { StandardSchemaV1Issue } from "@tanstack/vue-form"
77
77
  import { computed, getCurrentInstance } from "vue"
78
78
  import { useIntl } from "../../utils"
79
- import { useOmegaErrors } from "./OmegaErrorsContext"
79
+ import { type OmegaError } from "./OmegaFormStuff"
80
80
 
81
81
  const instance = getCurrentInstance()
82
82
  const vuetified = instance?.appContext.components["VAlert"]
83
83
 
84
- const { errors, generalErrors, showErrors } = useOmegaErrors()
84
+ const props = defineProps<
85
+ {
86
+ errors: readonly OmegaError[]
87
+ generalErrors: (Record<string, StandardSchemaV1Issue[]> | undefined)[] | undefined
88
+ showErrors: boolean
89
+ }
90
+ >()
85
91
 
86
92
  const { trans } = useIntl()
87
93
 
88
94
  const showedGeneralErrors = computed(() => {
89
- if (!generalErrors.value) return []
95
+ if (!props.generalErrors) return []
90
96
 
91
- return generalErrors
92
- .value
97
+ return props
98
+ .generalErrors
93
99
  .filter((record): record is Record<string, StandardSchemaV1Issue[]> => Boolean(record))
94
100
  .flatMap((errorRecord) =>
95
101
  Object
@@ -17,9 +17,11 @@
17
17
  <script
18
18
  setup
19
19
  lang="ts"
20
- generic="From extends Record<PropertyKey, any>,
20
+ generic="
21
+ From extends Record<PropertyKey, any>,
21
22
  To extends Record<PropertyKey, any>,
22
- Name extends DeepKeys<From>"
23
+ Name extends DeepKeys<From>
24
+ "
23
25
  >
24
26
  import { type DeepKeys } from "@tanstack/vue-form"
25
27
  import { inject } from "vue"
@@ -2,7 +2,7 @@ import { type Effect, Option, pipe, type Record, S } from "effect-app"
2
2
  /* eslint-disable @typescript-eslint/no-explicit-any */
3
3
  import { type DeepKeys, type FieldAsyncValidateOrFn, type FieldValidateOrFn, type FormApi, type FormAsyncValidateOrFn, type FormOptions, type FormState, type FormValidateOrFn, type StandardSchemaV1, type VueFormApi } from "@tanstack/vue-form"
4
4
  import { type RuntimeFiber } from "effect/Fiber"
5
- import { useIntl } from "../../utils"
5
+ import { getTransformationFrom, useIntl } from "../../utils"
6
6
  import { type OmegaFieldInternalApi } from "./InputProps"
7
7
  import { type OmegaFormReturn } from "./useOmegaForm"
8
8
 
@@ -253,8 +253,8 @@ export const createMeta = <T = any>(
253
253
  { meta = {}, parent = "", property, propertySignatures }: CreateMeta,
254
254
  acc: Partial<MetaRecord<T>> = {}
255
255
  ): MetaRecord<T> | FieldMeta => {
256
- // unwraps class..
257
- // TODO: might want to recursively unwrap.
256
+ // unwraps class (Class are transformations)
257
+ // this calls createMeta recursively, so wrapped transformations are also unwrapped
258
258
  if (property && property._tag === "Transformation") {
259
259
  return createMeta<T>({
260
260
  parent,
@@ -284,9 +284,8 @@ export const createMeta = <T = any>(
284
284
  .filter(
285
285
  (t) => t._tag !== "UndefinedKeyword" && t !== S.Null.ast
286
286
  )
287
- // unwrap classes..
288
- // TODO: might want to recursively unwrap.
289
- .map((_) => _._tag === "Transformation" ? _.from : _)
287
+ // unwraps class (Class are transformations)
288
+ .map(getTransformationFrom)
290
289
 
291
290
  const hasStructMembers = nonNullTypes.some(
292
291
  (t) => "propertySignatures" in t
@@ -1,6 +1,5 @@
1
1
  <template>
2
- <component
3
- :is="form.Field"
2
+ <form.Field
4
3
  :name="name"
5
4
  :validators="{
6
5
  onChange: schema,
@@ -22,15 +21,17 @@
22
21
  </template>
23
22
  </OmegaInternalInput>
24
23
  </template>
25
- </component>
24
+ </form.Field>
26
25
  </template>
27
26
 
28
27
  <script
29
28
  setup
30
29
  lang="ts"
31
- generic="// dprint ignore - somehow with 120 chars, this becomes a mess. should report it.
30
+ generic="
31
+ // dprint ignore - somehow with 120 chars, this becomes a mess. should report it.
32
32
  From extends Record<PropertyKey, any>,
33
- To extends Record<PropertyKey, any>"
33
+ To extends Record<PropertyKey, any>
34
+ "
34
35
  >
35
36
  import { computed, inject, type Ref } from "vue"
36
37
  import { useIntl } from "../../utils"
@@ -22,7 +22,6 @@
22
22
  import { type DeepKeys, useStore } from "@tanstack/vue-form"
23
23
  import { computed, type ComputedRef, getCurrentInstance, nextTick, onMounted, ref, useId, watch, watchEffect } from "vue"
24
24
  import type { InputProps, OmegaFieldInternalApi } from "./InputProps"
25
- import { useOmegaErrors } from "./OmegaErrorsContext"
26
25
  import type { FieldValidators, MetaRecord, NestedKeyOf, TypeOverride } from "./OmegaFormStuff"
27
26
  import OmegaInputVuetify from "./OmegaInputVuetify.vue"
28
27
 
@@ -93,7 +92,7 @@ onMounted(() => {
93
92
  fieldApi.setValue(null as any)
94
93
  }
95
94
  })
96
- const { addError, removeError, showErrors, showErrorsOn } = useOmegaErrors()
95
+ const { addError, removeError, showErrors, showErrorsOn } = (props.field.form as any).errorContext // todo; update types to include extended Omega Form props
97
96
 
98
97
  const realDirty = ref(false)
99
98
 
@@ -1,25 +1,10 @@
1
1
  <template>
2
2
  <form
3
3
  novalidate
4
- @submit.prevent.stop="formToUse.handleSubmit()"
4
+ @submit.prevent.stop="form.handleSubmit()"
5
5
  >
6
- <fieldset :disabled="formIsSubmitting">
7
- <!-- Render externalForm + default slots if props.form is provided -->
8
- <template v-if="props.form">
9
- <slot
10
- name="externalForm"
11
- :subscribed-values="subscribedValues"
12
- />
13
- <slot />
14
- <!-- default slot -->
15
- </template>
16
- <!-- Render internalForm slot if form was created locally -->
17
- <slot
18
- v-else-if="localForm"
19
- name="internalForm"
20
- :form="localForm"
21
- :subscribed-values="subscribedValues"
22
- />
6
+ <fieldset :disabled="formIsSubmitting || disabled">
7
+ <slot :subscribed-values="subscribedValues" />
23
8
  </fieldset>
24
9
  </form>
25
10
  </template>
@@ -27,258 +12,49 @@
27
12
  <script
28
13
  setup
29
14
  lang="ts"
30
- generic="From extends Record<PropertyKey, any>,
15
+ generic="
16
+ From extends Record<PropertyKey, any>,
31
17
  To extends Record<PropertyKey, any>,
32
18
  K extends keyof OmegaFormState<From, To> = keyof OmegaFormState<From, To>,
33
- Props = DefaultInputProps<From>"
19
+ Props = DefaultInputProps<From>
20
+ "
34
21
  >
35
22
  /**
36
23
  * Form component that wraps TanStack Form's useForm hook
37
24
  *
38
25
  * Usage:
39
- * <Form :default-values="..." :on-submit="..." :validators="..." ...etc>
40
- * <template #default="{ form }">
41
- * <!-- Children with access to form -->
42
- * <component :is="form.Field" name="fieldName">
43
- * <template #default="{ field }">
44
- * <input
45
- * :value="field.state.value"
46
- * @input="e => field.handleChange(e.target.value)"
47
- * />
48
- * </template>
49
- * </component>
50
- * </template>
51
- * </Form>
52
- *
53
- * <Form :default-values="..." :on-submit="..." :validators="..." ...etc>
54
- * <template #default="{ form }">
55
- * <Input :form="form" name="foobar" />
56
- * </template>
57
- * </Form>
58
- *
59
- * <Form :schema="schema" :subscribe="['values', 'isSubmitting']">
60
- * <template #default="{ form, subscribedValues }">
61
- * <Input :form="form" name="foobar" />
62
- * </template>
63
- * </Form>
26
+ * <form.Form>
27
+ * <form.Input name="foobar" />
28
+ * <form.Errors />
29
+ * </form.Form>
64
30
  */
65
31
  /* eslint-disable @typescript-eslint/no-explicit-any */
66
- import { type StandardSchemaV1Issue, useStore } from "@tanstack/vue-form"
67
- import { type Record, type S } from "effect-app"
68
- import { computed, getCurrentInstance, onBeforeMount, provide, watch } from "vue"
32
+ import { useStore } from "@tanstack/vue-form"
69
33
  import { getOmegaStore } from "./getOmegaStore"
70
- import { provideOmegaErrors } from "./OmegaErrorsContext"
71
- import { type FilterItems, type FormProps, type OmegaFormApi, type OmegaFormState, type ShowErrorsOn } from "./OmegaFormStuff"
72
- import { type DefaultInputProps, type OmegaConfig, OmegaFormKey, type OmegaFormReturn, useOmegaForm } from "./useOmegaForm"
73
-
74
- type OnSubmit = NonNullable<FormProps<From, To>["onSubmit"]>
75
- type OnSubmitArg = Parameters<OnSubmit>[0]
76
- type OnSubmitEmit = (value: To, extra: Pick<OnSubmitArg, "formApi" | "meta">) => void
77
- type OnSubmitAwaitable = (value: To, extra: Pick<OnSubmitArg, "formApi" | "meta">) => ReturnType<OnSubmit>
78
-
79
- type OmegaWrapperProps =
80
- & {
81
- omegaConfig?: OmegaConfig<From>
82
- subscribe?: K[]
83
- showErrorsOn?: ShowErrorsOn
84
- }
85
- & Omit<FormProps<From, To>, "onSubmit">
86
- & (
87
- | {
88
- form: OmegaFormReturn<From, To, Props>
89
- schema?: undefined
90
- }
91
- | {
92
- form?: undefined
93
- schema: S.Schema<To, From, never>
94
- }
95
- )
96
- & (
97
- | {
98
- isLoading: boolean
99
- onSubmit: OnSubmitEmit
100
- }
101
- | {
102
- isLoading?: undefined
103
- onSubmit?: OnSubmitAwaitable
104
- }
105
- )
106
-
107
- const props = withDefaults(defineProps<OmegaWrapperProps>(), {
108
- isLoading: undefined
109
- })
110
-
111
- const instance = getCurrentInstance()
112
-
113
- // we prefer to use the standard abstraction in Vue which separates props (going down) and event emits (going back up)
114
- // so if isLoading + @submit are provided, we wrap them into a Promise, so that TanStack Form can properly track the submitting state.
115
- // we use this approach because it means we can keep relying on the built-in beaviour of TanStack Form, and we dont have to re-implement/keep in sync/break any internals.
116
- const eventOnSubmit: OnSubmitAwaitable = (value, extra) =>
117
- new Promise<void>((resolve) => {
118
- instance!.emit("submit", value, extra)
119
- // even if the emit would be immediately handled, prop changes are not published/received immediately.
120
- // so we have to wait for the prop to change to true, and back to false again.
121
- const handle = watch(() => props.isLoading, (v) => {
122
- if (v) return
123
- resolve()
124
- handle.stop()
125
- })
126
- })
127
-
128
- // we need to keep onSubmit reactive or it can't pick up local state changes, inside forms
129
- // which is the whole point of having props
130
- const onSubmit_ = typeof props.isLoading !== "undefined"
131
- ? computed(() => eventOnSubmit)
132
- : typeof props.onSubmit !== "undefined"
133
- ? computed(() => props.onSubmit)
134
- : undefined
135
-
136
- const onSubmitHandler = onSubmit_?.value
137
- ? ({ formApi, meta, value }: OnSubmitArg) => onSubmit_!.value!(value, { meta, formApi })
138
- : undefined
139
-
140
- const localForm = props.form || !props.schema
141
- ? undefined
142
- : useOmegaForm<From, To>(
143
- props.schema,
144
- {
145
- ...props,
146
- onSubmit: onSubmitHandler
147
- },
148
- props.omegaConfig
149
- )
150
-
151
- const formToUse = computed(() => props.form ?? localForm!)
152
-
153
- onBeforeMount(() => {
154
- if (!props.form) return
155
- const formOptionsKeys = Object.keys(props.form.options || {})
156
-
157
- const excludedKeys: Set<keyof typeof props> = new Set([
158
- "omegaConfig",
159
- "subscribe",
160
- "showErrorsOn",
161
- "asyncAlways",
162
- "form",
163
- "schema",
164
- "canSubmitWhenInvalid"
165
- ])
166
-
167
- const filteredProps = Object.fromEntries(
168
- Object.entries(props).filter(
169
- ([key, value]) => {
170
- if (key === "isLoading") {
171
- return false
172
- }
173
- return !excludedKeys.has(key as keyof typeof props)
174
- && value !== undefined
175
- }
176
- )
177
- ) as Record<string, unknown>
34
+ import { type OmegaFormApi, type OmegaFormState } from "./OmegaFormStuff"
35
+ import { type DefaultInputProps, type OmegaFormReturn } from "./useOmegaForm"
178
36
 
179
- const propsKeys = Object.keys(filteredProps)
180
-
181
- const overlappingKeys = formOptionsKeys.filter(
182
- (key) =>
183
- propsKeys.includes(key)
184
- && filteredProps[key] !== undefined
185
- )
186
-
187
- if (overlappingKeys.length > 0) {
188
- console.warn(
189
- `[OmegaWrapper] Overlapping keys found between form options and filtered props:\n${
190
- overlappingKeys.join(
191
- ", \n"
192
- )
193
- }.\nProps will overwrite existing form options. This might indicate a configuration issue.`
194
- )
195
- }
196
-
197
- const mergedOptions = {
198
- ...formToUse.value.options,
199
- ...filteredProps
200
- }
201
- if (onSubmitHandler) mergedOptions.onSubmit = onSubmitHandler
37
+ type OmegaWrapperProps = {
38
+ form: OmegaFormReturn<From, To, Props>
39
+ disabled?: boolean
40
+ subscribe?: K[]
41
+ }
202
42
 
203
- formToUse.value.options = Object.fromEntries(
204
- // TODO
205
- (Object.entries(mergedOptions) as any).filter(
206
- ([_, value]: any) => value !== undefined
207
- )
208
- )
209
- })
43
+ const props = defineProps<OmegaWrapperProps>()
210
44
 
211
45
  const formIsSubmitting = useStore(
212
- formToUse.value.store,
46
+ props.form.store,
213
47
  (state) => state.isSubmitting
214
48
  )
215
49
 
216
50
  const subscribedValues = getOmegaStore(
217
- formToUse.value as unknown as OmegaFormApi<From, To>,
51
+ props.form as unknown as OmegaFormApi<From, To>,
218
52
  props.subscribe
219
53
  )
220
54
 
221
- const formSubmissionAttempts = useStore(
222
- formToUse.value.store,
223
- (state) => state.submissionAttempts
224
- )
225
-
226
- const errors = computed(() => formToUse.value.useStore((state) => state.errors))
227
-
228
- watch(
229
- () => [formToUse.value.filterItems, errors.value.value],
230
- () => {
231
- const filterItems: FilterItems | undefined = formToUse.value.filterItems
232
- const currentErrors = errors.value.value
233
- if (!filterItems) return {}
234
- if (!currentErrors) return {}
235
- const errorList = Object
236
- .values(currentErrors)
237
- .filter(
238
- (fieldErrors): fieldErrors is Record<string, StandardSchemaV1Issue[]> => Boolean(fieldErrors)
239
- )
240
- .flatMap((fieldErrors) =>
241
- Object
242
- .values(fieldErrors)
243
- .flat()
244
- .map((issue: StandardSchemaV1Issue) => issue.message)
245
- )
246
-
247
- if (errorList.some((e) => e === filterItems.message)) {
248
- // TODO: Investigate if filterItems.items should be typed based on DeepKeys<To>.
249
- filterItems.items.forEach((item: keyof From) => {
250
- const m = formToUse.value.getFieldMeta(item as any)
251
- if (m) {
252
- formToUse.value.setFieldMeta(item as any, {
253
- ...m,
254
- errorMap: {
255
- onSubmit: [
256
- { path: [item as string], message: filterItems.message }
257
- ]
258
- }
259
- })
260
- }
261
- })
262
- }
263
- return {}
264
- }
265
- )
266
-
267
- provideOmegaErrors(formSubmissionAttempts, errors.value, props.showErrorsOn)
268
-
269
55
  defineSlots<{
270
- // Default slot (no props)
271
- default(): void
272
- // Named slot when form is created internally via schema
273
- internalForm(props: {
274
- form: OmegaFormReturn<From, To>
275
- subscribedValues: typeof subscribedValues.value
276
- }): void
277
- // Named slot when form is passed via props (provides subscribedValues)
278
- externalForm(props: { subscribedValues: typeof subscribedValues.value }): void
56
+ default(props: { subscribedValues: typeof subscribedValues.value }): void
279
57
  }>()
280
-
281
- provide(OmegaFormKey, formToUse.value)
282
58
  </script>
283
59
 
284
60
  <style scoped>
@@ -1,13 +1,7 @@
1
- import { default as OmegaArray } from "./OmegaArray.vue"
2
- import { default as OmegaAutoGen } from "./OmegaAutoGen.vue"
3
- import { default as OmegaErrors } from "./OmegaErrors.vue"
4
- import { default as OmegaFormInput } from "./OmegaFormInput.vue"
5
1
  import { default as OmegaInput } from "./OmegaInput.vue"
6
- import { default as OmegaForm } from "./OmegaWrapper.vue"
7
2
 
8
3
  export * as OmegaErrorsContext from "./OmegaErrorsContext"
9
4
  export * from "./OmegaFormStuff"
10
- export { default } from "./OmegaWrapper.vue"
11
5
  export { type OmegaFormReturn, useOmegaForm } from "./useOmegaForm"
12
6
 
13
- export { OmegaArray, OmegaAutoGen, OmegaErrors, OmegaForm, OmegaFormInput, OmegaInput }
7
+ export { OmegaInput }