@ekz/formix 2.1.1 → 2.1.2

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 (73) hide show
  1. package/lib/cjs/FieldState.d.ts +17 -0
  2. package/lib/cjs/FieldState.js +33 -0
  3. package/lib/cjs/FieldState.js.map +1 -0
  4. package/lib/cjs/fields.d.ts +15 -0
  5. package/lib/cjs/fields.js +121 -0
  6. package/lib/cjs/fields.js.map +1 -0
  7. package/lib/cjs/form.d.ts +13 -0
  8. package/lib/cjs/form.js +119 -0
  9. package/lib/cjs/form.js.map +1 -0
  10. package/lib/cjs/index.d.ts +5 -0
  11. package/lib/cjs/index.js +25 -0
  12. package/lib/cjs/index.js.map +1 -0
  13. package/lib/cjs/references.d.ts +5 -0
  14. package/lib/cjs/references.js +34 -0
  15. package/lib/cjs/references.js.map +1 -0
  16. package/lib/cjs/types.d.ts +111 -0
  17. package/lib/cjs/types.js +3 -0
  18. package/lib/cjs/types.js.map +1 -0
  19. package/lib/cjs/validation/fields.d.ts +5 -0
  20. package/lib/cjs/validation/fields.js +114 -0
  21. package/lib/cjs/validation/fields.js.map +1 -0
  22. package/lib/cjs/validation/form.d.ts +2 -0
  23. package/lib/cjs/validation/form.js +62 -0
  24. package/lib/cjs/validation/form.js.map +1 -0
  25. package/lib/esm/FieldState.d.ts +17 -0
  26. package/lib/esm/FieldState.js +30 -0
  27. package/lib/esm/FieldState.js.map +1 -0
  28. package/lib/esm/fields.d.ts +15 -0
  29. package/lib/esm/fields.js +114 -0
  30. package/lib/esm/fields.js.map +1 -0
  31. package/lib/esm/form.d.ts +13 -0
  32. package/lib/esm/form.js +111 -0
  33. package/lib/esm/form.js.map +1 -0
  34. package/lib/esm/index.d.ts +5 -0
  35. package/lib/esm/index.js +5 -0
  36. package/lib/esm/index.js.map +1 -0
  37. package/lib/esm/references.d.ts +5 -0
  38. package/lib/esm/references.js +27 -0
  39. package/lib/esm/references.js.map +1 -0
  40. package/lib/esm/types.d.ts +111 -0
  41. package/lib/esm/types.js +2 -0
  42. package/lib/esm/types.js.map +1 -0
  43. package/lib/esm/validation/fields.d.ts +5 -0
  44. package/lib/esm/validation/fields.js +107 -0
  45. package/lib/esm/validation/fields.js.map +1 -0
  46. package/lib/esm/validation/form.d.ts +2 -0
  47. package/lib/esm/validation/form.js +58 -0
  48. package/lib/esm/validation/form.js.map +1 -0
  49. package/lib/esnext/FieldState.d.ts +17 -0
  50. package/lib/esnext/FieldState.js +34 -0
  51. package/lib/esnext/FieldState.js.map +1 -0
  52. package/lib/esnext/fields.d.ts +15 -0
  53. package/lib/esnext/fields.js +114 -0
  54. package/lib/esnext/fields.js.map +1 -0
  55. package/lib/esnext/form.d.ts +13 -0
  56. package/lib/esnext/form.js +111 -0
  57. package/lib/esnext/form.js.map +1 -0
  58. package/lib/esnext/index.d.ts +5 -0
  59. package/lib/esnext/index.js +5 -0
  60. package/lib/esnext/index.js.map +1 -0
  61. package/lib/esnext/references.d.ts +5 -0
  62. package/lib/esnext/references.js +29 -0
  63. package/lib/esnext/references.js.map +1 -0
  64. package/lib/esnext/types.d.ts +111 -0
  65. package/lib/esnext/types.js +2 -0
  66. package/lib/esnext/types.js.map +1 -0
  67. package/lib/esnext/validation/fields.d.ts +5 -0
  68. package/lib/esnext/validation/fields.js +107 -0
  69. package/lib/esnext/validation/fields.js.map +1 -0
  70. package/lib/esnext/validation/form.d.ts +2 -0
  71. package/lib/esnext/validation/form.js +58 -0
  72. package/lib/esnext/validation/form.js.map +1 -0
  73. package/package.json +3 -3
@@ -0,0 +1,5 @@
1
+ export { useField, useArrayField, UseField, UseArrayField } from './fields';
2
+ export { FormProvider, UseForm, useForm, useFormWithFields, withForm } from './form';
3
+ export { defineField, isFieldRef, defineArrayField, isArrayFieldRef } from './references';
4
+ export { useAsyncFieldStateValidation, useAsyncFieldValidation, useFieldStateValidation, useFieldValidation } from './validation/fields';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/main/web/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE5E,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAErF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE1F,OAAO,EACH,4BAA4B,EAC5B,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EACrB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ArrayFieldRef, FieldRef, GenericFieldRef } from './types';
2
+ export declare function defineField<V>(initialValue: V, initialDisabled?: boolean): FieldRef<V>;
3
+ export declare function isFieldRef(field: unknown): field is FieldRef<unknown>;
4
+ export declare function defineArrayField<T extends GenericFieldRef, V>(initialItems: Iterable<V>, itemTemplate: (value: V) => T, initialDisabled?: boolean): ArrayFieldRef<T, V>;
5
+ export declare function isArrayFieldRef(field: unknown): field is ArrayFieldRef<GenericFieldRef, unknown>;
@@ -0,0 +1,29 @@
1
+ import { List, Seq } from 'immutable';
2
+ import FieldStateImpl from './FieldState';
3
+ class FieldRefImpl {
4
+ initialState;
5
+ constructor(initialValue, initialDisabled) {
6
+ this.initialState = new FieldStateImpl(initialValue, initialDisabled, false, false);
7
+ }
8
+ }
9
+ export function defineField(initialValue, initialDisabled = false) {
10
+ return new FieldRefImpl(initialValue, initialDisabled);
11
+ }
12
+ export function isFieldRef(field) {
13
+ return field instanceof FieldRefImpl;
14
+ }
15
+ class ArrayFieldRefImpl extends FieldRefImpl {
16
+ itemTemplate;
17
+ constructor(initialItems, itemTemplate, initialDisabled) {
18
+ super(initialItems, initialDisabled);
19
+ this.itemTemplate = itemTemplate;
20
+ this.itemTemplate = itemTemplate;
21
+ }
22
+ }
23
+ export function defineArrayField(initialItems, itemTemplate, initialDisabled = false) {
24
+ return new ArrayFieldRefImpl(Seq(initialItems).map(itemTemplate).toList(), itemTemplate, initialDisabled);
25
+ }
26
+ export function isArrayFieldRef(field) {
27
+ return field instanceof ArrayFieldRefImpl;
28
+ }
29
+ //# sourceMappingURL=references.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"references.js","sourceRoot":"","sources":["../../src/main/web/references.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,cAAc,MAAM,cAAc,CAAC;AAG1C,MAAM,YAAY;IACL,YAAY,CAAgB;IAErC,YAAY,YAAe,EAAE,eAAwB;QACjD,IAAI,CAAC,YAAY,GAAG,IAAI,cAAc,CAAC,YAAY,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACxF,CAAC;CACJ;AAED,MAAM,UAAU,WAAW,CAAI,YAAe,EAAE,eAAe,GAAG,KAAK;IACnE,OAAO,IAAI,YAAY,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAc;IACrC,OAAO,KAAK,YAAY,YAAY,CAAC;AACzC,CAAC;AAED,MAAM,iBACF,SAAQ,YAAqB;IAKhB;IAFb,YACI,YAAqB,EACZ,YAA6B,EACtC,eAAwB;QAExB,KAAK,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;QAH5B,iBAAY,GAAZ,YAAY,CAAiB;QAItC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,CAAC;CACJ;AAED,MAAM,UAAU,gBAAgB,CAC5B,YAAyB,EACzB,YAA6B,EAC7B,eAAe,GAAG,KAAK;IAEvB,OAAO,IAAI,iBAAiB,CACxB,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,EAC5C,YAAY,EACZ,eAAe,CAClB,CAAC;AACN,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAc;IAC1C,OAAO,KAAK,YAAY,iBAAiB,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,111 @@
1
+ import type { Map, List, OrderedSet } from 'immutable';
2
+ export type GenericFieldRef = ArrayFieldRef<GenericFieldRef, unknown> | FieldRef<unknown> | {
3
+ [key: string]: GenericFieldRef;
4
+ };
5
+ export interface FieldRef<T> {
6
+ readonly initialState: FieldState<T>;
7
+ }
8
+ export interface ArrayFieldRef<T extends GenericFieldRef, V> extends FieldRef<List<T>> {
9
+ itemTemplate(value: V): T;
10
+ }
11
+ /**
12
+ * `FieldState` represents the state of a `FieldRef`
13
+ */
14
+ export interface FieldState<T> extends FieldValue<T>, FieldMetadata {
15
+ /**
16
+ * Value of the field
17
+ */
18
+ readonly value: T;
19
+ /**
20
+ * `true` when field is disabled
21
+ */
22
+ readonly disabled: boolean;
23
+ /**
24
+ * `true` when field is focused
25
+ */
26
+ readonly active: boolean;
27
+ /**
28
+ * `true` when field has been focused once
29
+ */
30
+ readonly touched: boolean;
31
+ updated(fields: Partial<FieldValue<T> & FieldMetadata>): FieldState<T>;
32
+ /**
33
+ * `equals` only takes the value and disabled properties into consideration.
34
+ */
35
+ equals(other: FieldState<T> | null | undefined): boolean;
36
+ /**
37
+ * `hashCode` only takes the value and disabled properties into consideration.
38
+ */
39
+ hashCode(): number;
40
+ }
41
+ export interface FieldValue<T> {
42
+ readonly value: T;
43
+ }
44
+ export interface FieldMetadata {
45
+ readonly disabled: boolean;
46
+ readonly active: boolean;
47
+ readonly touched: boolean;
48
+ }
49
+ export interface FieldMetadataUpdaters {
50
+ setDisabled(disabled: boolean): void;
51
+ onFocus(): void;
52
+ onBlur(): void;
53
+ }
54
+ export interface Field<T> extends FieldValue<T>, FieldMetadata, FieldMetadataUpdaters, ValidationResult {
55
+ setValue(value: React.SetStateAction<T>): void;
56
+ }
57
+ export interface ArrayField<T extends GenericFieldRef, V> extends FieldMetadata, FieldMetadataUpdaters, ValidationResult {
58
+ readonly items: List<T>;
59
+ unshift(value: V): T;
60
+ push(value: V): T;
61
+ insert(index: number, value: V): T;
62
+ remove(item: T): void;
63
+ swap(indexA: number, indexB: number): void;
64
+ }
65
+ export type FormStates<F> = F extends ArrayFieldRef<infer T, unknown> ? List<FormStates<T>> : F extends FieldRef<infer T> ? FieldState<T> : F extends {
66
+ [K: string]: GenericFieldRef;
67
+ } ? {
68
+ [K in keyof F]: FormStates<F[K]>;
69
+ } : never;
70
+ export type FieldRefStates<T extends GenericFieldRef[]> = {
71
+ [K in keyof T]: FormStates<T[K]>;
72
+ };
73
+ export type FormValues<F> = F extends ArrayFieldRef<infer T, unknown> ? List<FormValues<T>> : F extends FieldRef<infer T> ? T : F extends {
74
+ [K: string]: GenericFieldRef;
75
+ } ? {
76
+ [K in keyof F]: FormValues<F[K]>;
77
+ } : never;
78
+ export type FieldRefValues<T extends GenericFieldRef[]> = {
79
+ [K in keyof T]: FormValues<T[K]>;
80
+ };
81
+ export interface FormApi extends FormValidationApi {
82
+ getFieldState<T>(fieldRef: FieldRef<T>): FieldState<T>;
83
+ getFieldValue<T>(fieldRef: FieldRef<T>): T;
84
+ setFieldState<T>(fieldRef: FieldRef<T>, state: React.SetStateAction<FieldState<T>>): void;
85
+ getStates<F extends GenericFieldRef>(fields: F): FormStates<F>;
86
+ getValues<F extends GenericFieldRef>(fields: F): FormValues<F>;
87
+ reset(): void;
88
+ modified: boolean;
89
+ }
90
+ export interface FormWithFields<F extends GenericFieldRef> extends FormApi {
91
+ fields: F;
92
+ }
93
+ /**
94
+ * A ReactElement or a string represent an error message, and null represents no errors.
95
+ */
96
+ export type ValidationError = React.ReactElement | string | null;
97
+ export interface ValidationResult {
98
+ readonly error: ValidationError;
99
+ readonly pending: boolean;
100
+ }
101
+ export interface FieldValidation<T> extends ValidationResult {
102
+ readonly fieldRef: FieldRef<T>;
103
+ }
104
+ export type FormValidations = Map<FieldRef<unknown>, OrderedSet<FieldValidation<unknown>>>;
105
+ export interface FormValidationApi {
106
+ registerValidation(validation: FieldValidation<unknown>): () => void;
107
+ getValidationResult(fieldRef: FieldRef<unknown>): ValidationResult;
108
+ isFieldValid(fieldRef: GenericFieldRef): boolean;
109
+ valid: boolean;
110
+ pending: boolean;
111
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/main/web/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import type { GenericFieldRef, FormValues, FormStates, FieldRef, FieldRefValues, FieldRefStates, ValidationError } from '../types';
2
+ export declare function useFieldValidation<F extends FieldRef<unknown>, D extends GenericFieldRef[] = []>(fieldRef: F, validate: (value: FormValues<F>, ds: [...FieldRefValues<D>]) => ValidationError, deps?: [...D]): void;
3
+ export declare function useAsyncFieldValidation<F extends FieldRef<unknown>, D extends GenericFieldRef[] = []>(fieldRef: F, validate: (value: FormValues<F>, ds: [...FieldRefValues<D>]) => Promise<ValidationError>, deps?: [...D], debounceMs?: number): void;
4
+ export declare function useFieldStateValidation<F extends FieldRef<unknown>, D extends GenericFieldRef[] = []>(fieldRef: F, validate: (fieldState: FormStates<F>, dependentFieldStates: [...FieldRefStates<D>]) => ValidationError, deps?: [...D]): void;
5
+ export declare function useAsyncFieldStateValidation<F extends FieldRef<unknown>, D extends GenericFieldRef[] = []>(fieldRef: F, validate: (fieldState: FormStates<F>, ds: [...FieldRefStates<D>]) => Promise<ValidationError>, deps?: [...D], debounceMs?: number): void;
@@ -0,0 +1,107 @@
1
+ import * as React from 'react';
2
+ import { useForm } from '../form';
3
+ export function useFieldValidation(fieldRef, validate, deps) {
4
+ const { getValues, registerValidation } = useForm();
5
+ const value = getValues(fieldRef);
6
+ const dependentValues = React.useMemo(() => {
7
+ return (deps ?? []).map(getValues);
8
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9
+ }, [getValues, ...(deps ?? [])]);
10
+ const error = React.useMemo(() => {
11
+ return validate(value, dependentValues);
12
+ // validate should not be considered for changes
13
+ // eslint-disable-next-line react-hooks/exhaustive-deps
14
+ }, [value, ...dependentValues]);
15
+ const validation = React.useMemo(() => ({
16
+ fieldRef,
17
+ error,
18
+ pending: false
19
+ }), [fieldRef, error]);
20
+ React.useLayoutEffect(() => registerValidation(validation), [registerValidation, validation]);
21
+ }
22
+ function useAsyncValidationResult(validate, debounceMs, deps) {
23
+ const [result, setResult] = React.useState({
24
+ pending: false,
25
+ error: null
26
+ });
27
+ React.useEffect(() => {
28
+ let mounted = true;
29
+ setResult((previous) => ({ pending: true, error: previous.error }));
30
+ const timeoutId = setTimeout(() => {
31
+ validate()
32
+ .then((error) => {
33
+ if (mounted) {
34
+ setResult({ pending: false, error: error });
35
+ }
36
+ })
37
+ .catch((error) => {
38
+ if (mounted) {
39
+ setResult({ pending: false, error: 'Unexpected error' });
40
+ }
41
+ console.warn('[Unhandled Validation Error]', error);
42
+ console.warn('An async validator should never throw, but it should resolve with the validation result.');
43
+ });
44
+ }, debounceMs);
45
+ return () => {
46
+ mounted = false;
47
+ clearTimeout(timeoutId);
48
+ };
49
+ // validate should not be considered for changes
50
+ // eslint-disable-next-line react-hooks/exhaustive-deps
51
+ }, [debounceMs, ...deps]);
52
+ return result;
53
+ }
54
+ export function useAsyncFieldValidation(fieldRef, validate, deps, debounceMs = 200) {
55
+ const { getValues, registerValidation } = useForm();
56
+ const value = getValues(fieldRef);
57
+ const dependentValues = React.useMemo(() => {
58
+ return (deps ?? []).map(getValues);
59
+ // eslint-disable-next-line react-hooks/exhaustive-deps
60
+ }, [getValues, ...(deps ?? [])]);
61
+ const result = useAsyncValidationResult(() => {
62
+ return validate(value, dependentValues);
63
+ }, debounceMs, [value, ...dependentValues]);
64
+ const validation = React.useMemo(() => ({
65
+ fieldRef,
66
+ error: result.error,
67
+ pending: result.pending
68
+ }), [fieldRef, result.error, result.pending]);
69
+ React.useLayoutEffect(() => registerValidation(validation), [registerValidation, validation]);
70
+ }
71
+ export function useFieldStateValidation(fieldRef, validate, deps) {
72
+ const { getStates, registerValidation } = useForm();
73
+ const fieldState = getStates(fieldRef);
74
+ const dependentStates = React.useMemo(() => {
75
+ return (deps ?? []).map(getStates);
76
+ // eslint-disable-next-line react-hooks/exhaustive-deps
77
+ }, [getStates, ...(deps ?? [])]);
78
+ const error = React.useMemo(() => {
79
+ return validate(fieldState, dependentStates);
80
+ // validate should not be considered for changes
81
+ // eslint-disable-next-line react-hooks/exhaustive-deps
82
+ }, [fieldState, ...dependentStates]);
83
+ const validation = React.useMemo(() => ({
84
+ fieldRef,
85
+ error,
86
+ pending: false
87
+ }), [fieldRef, error]);
88
+ React.useLayoutEffect(() => registerValidation(validation), [registerValidation, validation]);
89
+ }
90
+ export function useAsyncFieldStateValidation(fieldRef, validate, deps, debounceMs = 200) {
91
+ const { getStates, registerValidation } = useForm();
92
+ const fieldState = getStates(fieldRef);
93
+ const dependentValues = React.useMemo(() => {
94
+ return (deps ?? []).map(getStates);
95
+ // eslint-disable-next-line react-hooks/exhaustive-deps
96
+ }, [getStates, ...(deps ?? [])]);
97
+ const result = useAsyncValidationResult(() => {
98
+ return validate(fieldState, dependentValues);
99
+ }, debounceMs, [fieldState, ...dependentValues]);
100
+ const validation = React.useMemo(() => ({
101
+ fieldRef,
102
+ error: result.error,
103
+ pending: result.pending
104
+ }), [fieldRef, result.error, result.pending]);
105
+ React.useLayoutEffect(() => registerValidation(validation), [registerValidation, validation]);
106
+ }
107
+ //# sourceMappingURL=fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../../src/main/web/validation/fields.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { FormApi, FormValidationApi } from '../types';
2
+ export declare function useFormValidation(getFieldState: FormApi['getFieldState']): FormValidationApi;
@@ -0,0 +1,58 @@
1
+ import { Seq, List, Map, OrderedSet } from 'immutable';
2
+ import * as React from 'react';
3
+ import { isArrayFieldRef, isFieldRef } from '../references';
4
+ export function useFormValidation(getFieldState) {
5
+ const [validations, setValidations] = React.useState(Map);
6
+ const registerValidation = React.useCallback((validation) => {
7
+ setValidations((previous) => previous.update(validation.fieldRef, OrderedSet(), (validations) => validations.add(validation)));
8
+ return () => setValidations((previous) => previous.update(validation.fieldRef, OrderedSet(), (validations) => validations.remove(validation)));
9
+ }, []);
10
+ const getValidationResult = React.useCallback((fieldRef) => {
11
+ const result = validations.get(fieldRef, OrderedSet()).reduce((result, validation) => ({
12
+ error: result.error || validation.error,
13
+ pending: result.pending || validation.pending
14
+ }), EmptyValidationResult);
15
+ // The reduce allocates a fresh object even for a passing field, which makes every
16
+ // `useField` on a validated field unstable across renders. Collapse the no-error,
17
+ // no-pending case back to the frozen singleton so identity stays stable.
18
+ return result.error == null && !result.pending ? EmptyValidationResult : result;
19
+ }, [validations]);
20
+ const isFieldValid = React.useCallback((fieldRef) => {
21
+ function flatten(field) {
22
+ if (isArrayFieldRef(field)) {
23
+ return getFieldState(field).value.flatMap(flatten);
24
+ }
25
+ else if (isFieldRef(field)) {
26
+ return List.of(field);
27
+ }
28
+ else {
29
+ return Seq.Keyed(field).valueSeq().flatMap(flatten).toList();
30
+ }
31
+ }
32
+ const fields = flatten(fieldRef).toSet();
33
+ return validations
34
+ .filter((_, field) => fields.has(field))
35
+ .valueSeq()
36
+ .flatMap((v) => v)
37
+ .every((v) => v.error == null && !v.pending);
38
+ }, [getFieldState, validations]);
39
+ const pending = React.useMemo(() => {
40
+ return validations
41
+ .valueSeq()
42
+ .flatMap((v) => v)
43
+ .some((v) => v.pending);
44
+ }, [validations]);
45
+ const valid = React.useMemo(() => {
46
+ return (!pending &&
47
+ validations
48
+ .valueSeq()
49
+ .flatMap((v) => v)
50
+ .every((v) => v.error == null));
51
+ }, [pending, validations]);
52
+ return { registerValidation, getValidationResult, isFieldValid, valid, pending };
53
+ }
54
+ const EmptyValidationResult = Object.freeze({
55
+ error: null,
56
+ pending: false
57
+ });
58
+ //# sourceMappingURL=form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.js","sourceRoot":"","sources":["../../../src/main/web/validation/form.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekz/formix",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "license": "MIT",
5
5
  "description": "Type-safe React form to be used with Typescript",
6
6
  "homepage": "https://docs.ekz.io/formix/",
@@ -37,7 +37,7 @@
37
37
  "type-safe"
38
38
  ],
39
39
  "peerDependencies": {
40
- "@ekz/option": "^2.0.1",
40
+ "@ekz/option": "^2.0.2",
41
41
  "immutable": "^4 || ^5",
42
42
  "react": "^16.8.0 || ^17 || ^18 || ^19",
43
43
  "typescript": "^4 || ^5 || ^6 "
@@ -71,7 +71,7 @@
71
71
  "dev": "yarn compile:esm --watch",
72
72
  "test": "yarn typecheck:tests && jest",
73
73
  "typecheck:tests": "tsc -p tsconfig.test.json",
74
- "prepublish": "yarn clean && yarn test --ci --no-watchman && yarn lint && yarn compile"
74
+ "prepack": "yarn clean && yarn test --ci --no-watchman && yarn lint && yarn compile"
75
75
  },
76
76
  "jest": {
77
77
  "preset": "ts-jest/presets/default",