@strictly/react-form 0.0.1 → 0.0.3
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.
- package/.out/core/mobx/field_adapter.d.ts +7 -6
- package/.out/core/mobx/field_adapter_builder.d.ts +12 -13
- package/.out/core/mobx/field_adapter_builder.js +8 -12
- package/.out/core/mobx/field_adapters_of_values.d.ts +4 -0
- package/.out/core/mobx/flattened_adapters_of_fields.d.ts +2 -2
- package/.out/core/mobx/flattened_list_types_of_type.d.ts +8 -0
- package/.out/core/mobx/form_fields_of_field_adapters.d.ts +8 -0
- package/.out/core/mobx/form_presenter.d.ts +21 -24
- package/.out/core/mobx/form_presenter.js +64 -69
- package/.out/core/mobx/merge_field_adapters_with_two_way_converter.d.ts +13 -0
- package/.out/core/mobx/merge_field_adapters_with_two_way_converter.js +11 -0
- package/.out/core/mobx/merge_field_adapters_with_validators.d.ts +11 -0
- package/.out/core/mobx/merge_field_adapters_with_validators.js +45 -0
- package/.out/core/mobx/specs/fixtures.d.ts +7 -0
- package/.out/core/mobx/specs/fixtures.js +20 -0
- package/.out/core/mobx/specs/flattened_adapters_of_fields.tests.js +5 -2
- package/.out/core/mobx/specs/{flattened_list_type_defs_of.tests.js → flattened_list_types_of_types.tests.js} +7 -7
- package/.out/core/mobx/specs/form_presenter.tests.js +162 -60
- package/.out/core/mobx/specs/merge_field_adapters_with_two_way_converter.js +89 -0
- package/.out/core/mobx/specs/merge_field_adapters_with_validators.tests.js +172 -0
- package/.out/core/mobx/types.d.ts +2 -2
- package/.out/field_converters/chain_field_converter.d.ts +3 -3
- package/.out/field_converters/chain_field_converter.js +17 -12
- package/.out/field_converters/identity_converter.d.ts +3 -3
- package/.out/field_converters/identity_converter.js +10 -6
- package/.out/field_converters/integer_to_string_converter.d.ts +5 -4
- package/.out/field_converters/integer_to_string_converter.js +13 -6
- package/.out/field_converters/list_converter.d.ts +2 -2
- package/.out/field_converters/list_converter.js +6 -1
- package/.out/field_converters/maybe_identity_converter.d.ts +3 -3
- package/.out/field_converters/maybe_identity_converter.js +3 -1
- package/.out/field_converters/nullable_to_boolean_converter.d.ts +9 -8
- package/.out/field_converters/nullable_to_boolean_converter.js +13 -7
- package/.out/field_converters/select_value_type_converter.d.ts +20 -15
- package/.out/field_converters/select_value_type_converter.js +29 -14
- package/.out/field_converters/specs/chain_field_converter.tests.d.ts +1 -0
- package/.out/field_converters/specs/chain_field_converter.tests.js +251 -0
- package/.out/field_converters/trimming_string_converter.d.ts +3 -3
- package/.out/field_converters/trimming_string_converter.js +7 -3
- package/.out/field_converters/validating_converter.d.ts +3 -3
- package/.out/field_converters/validating_converter.js +7 -5
- package/.out/index.d.ts +9 -2
- package/.out/index.js +9 -2
- package/.out/mantine/create_checkbox.d.ts +2 -3
- package/.out/mantine/create_checkbox.js +6 -5
- package/.out/mantine/create_pill.js +2 -2
- package/.out/mantine/create_radio.js +1 -1
- package/.out/mantine/create_radio_group.d.ts +2 -3
- package/.out/mantine/create_radio_group.js +4 -3
- package/.out/mantine/create_text_input.d.ts +2 -3
- package/.out/mantine/create_text_input.js +6 -5
- package/.out/mantine/create_value_input.d.ts +2 -3
- package/.out/mantine/create_value_input.js +6 -5
- package/.out/mantine/error_renderer.d.ts +6 -0
- package/.out/mantine/error_renderer.js +5 -0
- package/.out/mantine/hooks.d.ts +9 -13
- package/.out/mantine/hooks.js +10 -15
- package/.out/mantine/specs/checkbox_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/checkbox_hooks.stories.js +33 -6
- package/.out/mantine/specs/list_hooks.stories.js +2 -2
- package/.out/mantine/specs/radio_group_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/radio_group_hooks.stories.js +33 -6
- package/.out/mantine/specs/select_hooks.stories.d.ts +8 -2
- package/.out/mantine/specs/select_hooks.stories.js +45 -8
- package/.out/mantine/specs/text_input_hooks.stories.d.ts +5 -1
- package/.out/mantine/specs/text_input_hooks.stories.js +23 -8
- package/.out/mantine/specs/value_input_hooks.stories.d.ts +7 -2
- package/.out/mantine/specs/value_input_hooks.stories.js +49 -15
- package/.out/mantine/types.d.ts +4 -1
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.out/types/error_of_field.d.ts +2 -0
- package/.out/types/error_of_field.js +1 -0
- package/.out/types/field.d.ts +1 -1
- package/.out/types/field_converters.d.ts +17 -10
- package/.out/types/field_converters.js +5 -5
- package/.out/types/flattened_validators_of_fields.d.ts +8 -0
- package/.out/types/flattened_validators_of_fields.js +1 -0
- package/.out/types/merge_validators.d.ts +7 -0
- package/.out/types/merge_validators.js +38 -0
- package/.out/types/specs/flattened_validators_of_fields.tests.d.ts +1 -0
- package/.out/types/specs/flattened_validators_of_fields.tests.js +16 -0
- package/.out/types/specs/merge_validators.tests.d.ts +1 -0
- package/.out/types/specs/merge_validators.tests.js +192 -0
- package/.out/util/partial.d.ts +11 -5
- package/.out/util/partial.js +55 -15
- package/.turbo/turbo-build.log +9 -9
- package/.turbo/turbo-check-types.log +1 -1
- package/.turbo/turbo-release$colon$exports.log +1 -1
- package/README.md +5 -1
- package/core/mobx/field_adapter.ts +15 -7
- package/core/mobx/field_adapter_builder.ts +39 -75
- package/core/mobx/field_adapters_of_values.ts +17 -0
- package/core/mobx/flattened_adapters_of_fields.ts +3 -3
- package/core/mobx/flattened_list_types_of_type.ts +17 -0
- package/core/mobx/form_fields_of_field_adapters.ts +16 -0
- package/core/mobx/form_presenter.ts +117 -104
- package/core/mobx/merge_field_adapters_with_two_way_converter.ts +68 -0
- package/core/mobx/merge_field_adapters_with_validators.ts +99 -0
- package/core/mobx/specs/fixtures.ts +73 -0
- package/core/mobx/specs/flattened_adapters_of_fields.tests.ts +23 -2
- package/core/mobx/specs/flattened_list_types_of_types.tests.ts +35 -0
- package/core/mobx/specs/form_presenter.tests.ts +248 -124
- package/core/mobx/specs/merge_field_adapters_with_two_way_converter.ts +140 -0
- package/core/mobx/specs/merge_field_adapters_with_validators.tests.ts +259 -0
- package/core/mobx/types.ts +3 -3
- package/dist/index.cjs +527 -10412
- package/dist/index.d.cts +153 -111
- package/dist/index.d.ts +153 -111
- package/dist/index.js +527 -10420
- package/field_converters/chain_field_converter.ts +37 -23
- package/field_converters/identity_converter.ts +14 -10
- package/field_converters/integer_to_string_converter.ts +15 -9
- package/field_converters/list_converter.ts +8 -3
- package/field_converters/maybe_identity_converter.ts +7 -4
- package/field_converters/nullable_to_boolean_converter.ts +23 -16
- package/field_converters/select_value_type_converter.ts +86 -26
- package/field_converters/specs/chain_field_converter.tests.ts +302 -0
- package/field_converters/trimming_string_converter.ts +11 -6
- package/field_converters/validating_converter.ts +21 -11
- package/index.ts +9 -2
- package/mantine/create_checkbox.tsx +15 -8
- package/mantine/create_list.tsx +1 -4
- package/mantine/create_pill.tsx +2 -2
- package/mantine/create_radio.tsx +1 -1
- package/mantine/create_radio_group.tsx +8 -6
- package/mantine/create_text_input.tsx +20 -8
- package/mantine/create_value_input.tsx +17 -8
- package/mantine/error_renderer.ts +15 -0
- package/mantine/hooks.tsx +25 -51
- package/mantine/specs/__snapshots__/checkbox_hooks.tests.tsx.snap +126 -0
- package/mantine/specs/__snapshots__/radio_group_hooks.tests.tsx.snap +356 -0
- package/mantine/specs/__snapshots__/select_hooks.tests.tsx.snap +208 -12
- package/mantine/specs/__snapshots__/text_input_hooks.tests.tsx.snap +45 -0
- package/mantine/specs/__snapshots__/value_input_hooks.tests.tsx.snap +194 -8
- package/mantine/specs/checkbox_hooks.stories.tsx +47 -7
- package/mantine/specs/list_hooks.stories.tsx +2 -2
- package/mantine/specs/radio_group_hooks.stories.tsx +47 -7
- package/mantine/specs/select_hooks.stories.tsx +55 -8
- package/mantine/specs/text_input_hooks.stories.tsx +32 -7
- package/mantine/specs/value_input_hooks.stories.tsx +57 -16
- package/mantine/types.ts +5 -1
- package/package.json +20 -7
- package/tsconfig.json +1 -0
- package/types/error_of_field.ts +3 -0
- package/types/field.ts +1 -1
- package/types/field_converters.ts +21 -10
- package/types/flattened_validators_of_fields.ts +34 -0
- package/types/merge_validators.ts +80 -0
- package/types/specs/error_type_of_field.tests.ts +2 -2
- package/types/specs/flattened_validators_of_fields.tests.ts +93 -0
- package/types/specs/merge_validators.tests.ts +267 -0
- package/util/partial.tsx +200 -16
- package/.out/core/mobx/flattened_list_type_defs_of.d.ts +0 -8
- package/.out/field_validators/minimum_string_length_field_validator.d.ts +0 -2
- package/.out/field_validators/minimum_string_length_field_validator.js +0 -8
- package/.out/types/error_type_of_field.d.ts +0 -2
- package/.out/types/field_validator.d.ts +0 -3
- package/.out/types/flattened_form_fields_of.d.ts +0 -9
- package/.out/types/specs/flattened_form_fields_of.tests.js +0 -13
- package/core/mobx/flattened_list_type_defs_of.ts +0 -17
- package/core/mobx/specs/flattened_list_type_defs_of.tests.ts +0 -35
- package/field_validators/minimum_string_length_field_validator.ts +0 -13
- package/mantine/specs/__snapshots__/check_box_hooks.tests.tsx.snap +0 -227
- package/types/error_type_of_field.ts +0 -3
- package/types/field_validator.ts +0 -7
- package/types/flattened_form_fields_of.ts +0 -16
- package/types/specs/flattened_form_fields_of.tests.ts +0 -43
- /package/.out/core/mobx/{flattened_list_type_defs_of.js → field_adapters_of_values.js} +0 -0
- /package/.out/core/mobx/{specs/flattened_list_type_defs_of.tests.d.ts → flattened_list_types_of_type.js} +0 -0
- /package/.out/{types/error_type_of_field.js → core/mobx/form_fields_of_field_adapters.js} +0 -0
- /package/.out/{types/field_validator.js → core/mobx/specs/flattened_list_types_of_types.tests.d.ts} +0 -0
- /package/.out/{types/flattened_form_fields_of.js → core/mobx/specs/merge_field_adapters_with_two_way_converter.d.ts} +0 -0
- /package/.out/{types/specs/flattened_form_fields_of.tests.d.ts → core/mobx/specs/merge_field_adapters_with_validators.tests.d.ts} +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type AnnotatedFieldConverter, type FieldValueFactory, type UnreliableFieldConverter } from 'types/field_converters';
|
|
2
2
|
export type FieldAdapter<From = any, To = any, E = any, ValuePath extends string = any, Context = any> = {
|
|
3
|
-
readonly convert:
|
|
3
|
+
readonly convert: AnnotatedFieldConverter<From, To, ValuePath, Context>;
|
|
4
4
|
readonly create: FieldValueFactory<From, ValuePath, Context>;
|
|
5
|
-
readonly revert?:
|
|
5
|
+
readonly revert?: UnreliableFieldConverter<To, From, E, ValuePath, Context>;
|
|
6
6
|
};
|
|
7
|
-
export type
|
|
8
|
-
export type
|
|
9
|
-
export type
|
|
7
|
+
export type FromOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer From> ? From : never;
|
|
8
|
+
export type ToOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer _F, infer To> ? To : never;
|
|
9
|
+
export type ErrorOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer _From, infer _To, infer E> ? NonNullable<E> : never;
|
|
10
|
+
export type ValuePathOfFieldAdapter<C extends FieldAdapter> = C extends FieldAdapter<infer _From, infer _To, infer _E, infer ValuePath> ? ValuePath : never;
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import { type
|
|
1
|
+
import { type AnnotatedFieldConverter, type FieldValueFactory, type TwoWayFieldConverter, type TwoWayFieldConverterWithValueFactory, type UnreliableFieldConverter } from 'types/field_converters';
|
|
2
|
+
import { type FieldAdapter } from './field_adapter';
|
|
3
3
|
declare class FieldAdapterBuilder<From, To, E, ValuePath extends string, Context> {
|
|
4
|
-
readonly convert:
|
|
4
|
+
readonly convert: AnnotatedFieldConverter<From, To, ValuePath, Context>;
|
|
5
5
|
readonly create: FieldValueFactory<From, ValuePath, Context>;
|
|
6
|
-
readonly revert?:
|
|
7
|
-
constructor(convert:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
chain<To2, E2 = E>(converter: SafeFieldConverter<To, To2, ValuePath, Context>, reverter?: FieldConverter<To2, To, E2, ValuePath, Context>): FieldAdapterBuilder<From, To2, E | E2, ValuePath, Context>;
|
|
11
|
-
withReverter(reverter: FieldConverter<To, From, E, ValuePath, Context>): FieldAdapterBuilder<From, To, E, ValuePath, Context>;
|
|
6
|
+
readonly revert?: UnreliableFieldConverter<To, From, E, ValuePath, Context> | undefined;
|
|
7
|
+
constructor(convert: AnnotatedFieldConverter<From, To, ValuePath, Context>, create: FieldValueFactory<From, ValuePath, Context>, revert?: UnreliableFieldConverter<To, From, E, ValuePath, Context> | undefined);
|
|
8
|
+
chain<To2, E2 = E>(converter: AnnotatedFieldConverter<To, To2, ValuePath, Context>, reverter?: UnreliableFieldConverter<To2, To, E2, ValuePath, Context>): FieldAdapterBuilder<From, To2, E | E2, ValuePath, Context>;
|
|
9
|
+
withReverter(reverter: UnreliableFieldConverter<To, From, E, ValuePath, Context>): FieldAdapterBuilder<From, To, E, ValuePath, Context>;
|
|
12
10
|
withIdentity(isFrom: (from: To | From) => from is From): FieldAdapterBuilder<From, To | From, E, ValuePath, Context>;
|
|
11
|
+
get narrow(): FieldAdapter<From, To, E, ValuePath, Context>;
|
|
13
12
|
}
|
|
14
|
-
export declare function adapter<From, To, ValuePath extends string, Context>(converter:
|
|
15
|
-
export declare function adapter<From, To, E, ValuePath extends string, Context>(converter:
|
|
13
|
+
export declare function adapter<From, To, ValuePath extends string, Context>(converter: AnnotatedFieldConverter<From, To, ValuePath, Context>, valueFactory: FieldValueFactory<From, ValuePath, Context>): FieldAdapterBuilder<From, To, never, ValuePath, Context>;
|
|
14
|
+
export declare function adapter<From, To, E, ValuePath extends string, Context>(converter: AnnotatedFieldConverter<From, To, ValuePath, Context>, valueFactory: FieldValueFactory<From, ValuePath, Context>, reverter: UnreliableFieldConverter<To, From, E, ValuePath, Context>): FieldAdapterBuilder<From, To, E, ValuePath, Context>;
|
|
16
15
|
export declare function adapterFromTwoWayConverter<From, To, E, ValuePath extends string, Context>(converter: TwoWayFieldConverter<From, To, E, ValuePath, Context>, valueFactory: FieldValueFactory<From, ValuePath, Context>): FieldAdapterBuilder<From, To, E, ValuePath, Context>;
|
|
17
16
|
export declare function adapterFromTwoWayConverter<From, To, E, ValuePath extends string, Context>(converter: TwoWayFieldConverterWithValueFactory<From, To, E, ValuePath, Context>): FieldAdapterBuilder<From, To, E, ValuePath, Context>;
|
|
18
|
-
export declare function adapterFromPrototype<From, To, ValuePath extends string, Context>(converter:
|
|
17
|
+
export declare function adapterFromPrototype<From, To, ValuePath extends string, Context>(converter: AnnotatedFieldConverter<From, To, ValuePath, Context>, prototype: From): FieldAdapterBuilder<From, To, never, ValuePath, Context>;
|
|
19
18
|
export declare function adapterFromPrototype<From, To, E, ValuePath extends string, Context>(converter: TwoWayFieldConverter<From, To, E, ValuePath, Context>, prototype: From): FieldAdapterBuilder<From, To, E, ValuePath, Context>;
|
|
20
|
-
export declare function identityAdapter<V, ValuePath extends string, Context>(prototype: V): FieldAdapterBuilder<V, V, never, ValuePath, Context>;
|
|
19
|
+
export declare function identityAdapter<V, ValuePath extends string, Context>(prototype: V, required?: boolean): FieldAdapterBuilder<V, V, never, ValuePath, Context>;
|
|
21
20
|
export declare function listAdapter<E, K extends string, ValuePath extends string, Context>(): FieldAdapterBuilder<readonly E[], readonly K[], never, ValuePath, Context>;
|
|
22
21
|
export {};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { chainAnnotatedFieldConverter, chainUnreliableFieldConverter, } from 'field_converters/chain_field_converter';
|
|
2
|
+
import { annotatedIdentityConverter, unreliableIdentityConverter, } from 'field_converters/identity_converter';
|
|
3
3
|
import { listConverter } from 'field_converters/list_converter';
|
|
4
4
|
import { MaybeIdentityConverter } from 'field_converters/maybe_identity_converter';
|
|
5
|
-
import { validatingConverter, } from 'field_converters/validating_converter';
|
|
6
5
|
import { prototypingFieldValueFactory } from 'field_value_factories/prototyping_field_value_factory';
|
|
7
6
|
class FieldAdapterBuilder {
|
|
8
7
|
convert;
|
|
@@ -13,14 +12,8 @@ class FieldAdapterBuilder {
|
|
|
13
12
|
this.create = create;
|
|
14
13
|
this.revert = revert;
|
|
15
14
|
}
|
|
16
|
-
validateFrom(...validators) {
|
|
17
|
-
return new FieldAdapterBuilder(this.convert, this.create, this.revert && chainFieldConverter(this.revert, validatingConverter(validators)));
|
|
18
|
-
}
|
|
19
|
-
validateTo(...validators) {
|
|
20
|
-
return new FieldAdapterBuilder(this.convert, this.create, this.revert && chainFieldConverter(validatingConverter(validators), this.revert));
|
|
21
|
-
}
|
|
22
15
|
chain(converter, reverter) {
|
|
23
|
-
return new FieldAdapterBuilder(
|
|
16
|
+
return new FieldAdapterBuilder(chainAnnotatedFieldConverter(this.convert, converter), this.create, this.revert && reverter && chainUnreliableFieldConverter(reverter, this.revert));
|
|
24
17
|
}
|
|
25
18
|
withReverter(reverter) {
|
|
26
19
|
return new FieldAdapterBuilder(this.convert, this.create, reverter);
|
|
@@ -33,6 +26,9 @@ class FieldAdapterBuilder {
|
|
|
33
26
|
}, isFrom);
|
|
34
27
|
return new FieldAdapterBuilder(identityConverter.convert.bind(identityConverter), this.create, this.revert && identityConverter.revert.bind(identityConverter));
|
|
35
28
|
}
|
|
29
|
+
get narrow() {
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
36
32
|
}
|
|
37
33
|
export function adapter(converter, valueFactory, reverter) {
|
|
38
34
|
return new FieldAdapterBuilder(converter, valueFactory, reverter);
|
|
@@ -48,8 +44,8 @@ export function adapterFromPrototype(converter, prototype) {
|
|
|
48
44
|
? new FieldAdapterBuilder(converter, factory)
|
|
49
45
|
: new FieldAdapterBuilder(converter.convert.bind(converter), factory, converter.revert.bind(converter));
|
|
50
46
|
}
|
|
51
|
-
export function identityAdapter(prototype) {
|
|
52
|
-
return new FieldAdapterBuilder(
|
|
47
|
+
export function identityAdapter(prototype, required) {
|
|
48
|
+
return new FieldAdapterBuilder(annotatedIdentityConverter(required), prototypingFieldValueFactory(prototype), unreliableIdentityConverter());
|
|
53
49
|
}
|
|
54
50
|
export function listAdapter() {
|
|
55
51
|
return new FieldAdapterBuilder(listConverter(), prototypingFieldValueFactory([]));
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type FieldAdapter } from './field_adapter';
|
|
2
|
+
export type FieldAdaptersOfValues<FlattenedValues extends Readonly<Record<string, any>>, TypePathsToValuePaths extends Readonly<Record<keyof FlattenedValues, string>> = Readonly<Record<keyof FlattenedValues, any>>, Context = any> = {
|
|
3
|
+
readonly [K in keyof FlattenedValues]: FieldAdapter<FlattenedValues[K], any, any, TypePathsToValuePaths[K], Context>;
|
|
4
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ReadonlyTypeOfType, type Type, type ValueOfType } from '@strictly/define';
|
|
2
2
|
import { type FieldAdapter } from 'core/mobx/field_adapter';
|
|
3
3
|
import { type SimplifyDeep, type ValueOf } from 'type-fest';
|
|
4
4
|
import { type Field } from 'types/field';
|
|
5
5
|
export type FlattenedAdaptersOfFields<ValuePathsToTypePaths extends Readonly<Record<string, string>>, FlattenedTypeDefs extends Partial<Readonly<Record<ValueOf<ValuePathsToTypePaths>, Type>>>, FormFields extends Partial<Readonly<Record<keyof ValuePathsToTypePaths, Field>>>> = SimplifyDeep<{
|
|
6
6
|
readonly [K in keyof ValuePathsToTypePaths as FormFields[K] extends Field ? ValuePathsToTypePaths[K] : never]: AdapterOfField<NonNullable<FormFields[K]>, FlattenedTypeDefs[ValuePathsToTypePaths[K]], K>;
|
|
7
7
|
}>;
|
|
8
|
-
type AdapterOfField<F extends Field, T extends Type | undefined, ValuePath extends string | number | symbol> = ValuePath extends string ? F extends Field<infer V, infer E> ? undefined extends T ? FieldAdapter<V, V, E, ValuePath> : FieldAdapter<
|
|
8
|
+
type AdapterOfField<F extends Field, T extends Type | undefined, ValuePath extends string | number | symbol> = ValuePath extends string ? F extends Field<infer V, infer E> ? undefined extends T ? FieldAdapter<V, V, E, ValuePath> : FieldAdapter<ValueOfType<ReadonlyTypeOfType<NonNullable<T>>>, V, E, ValuePath> : never : never;
|
|
9
9
|
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type FlattenedTypesOfType, type ListTypeDef, type Type } from '@strictly/define';
|
|
2
|
+
import { type SimplifyDeep } from 'type-fest';
|
|
3
|
+
export type ListValuePathsOfType<T extends Type> = keyof FlattenedListTypesOfType<T>;
|
|
4
|
+
export type FlattenedListTypesOfType<T extends Type> = FlattenedListTypesOfTypes<SimplifyDeep<FlattenedTypesOfType<T, null>>>;
|
|
5
|
+
type FlattenedListTypesOfTypes<T extends Readonly<Record<string, Type>>> = {
|
|
6
|
+
[K in keyof T as T[K]['definition'] extends ListTypeDef ? K : never]: T[K];
|
|
7
|
+
};
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ValueOf } from 'type-fest';
|
|
2
|
+
import { type Field } from 'types/field';
|
|
3
|
+
import { type FieldAdapter } from './field_adapter';
|
|
4
|
+
export type FormFieldsOfFieldAdapters<ValuePathsToTypePaths extends Readonly<Record<string, string>>, FieldAdapters extends Partial<Readonly<Record<ValueOf<ValuePathsToTypePaths>, FieldAdapter>>>> = {
|
|
5
|
+
[K in keyof ValuePathsToTypePaths as undefined extends FieldAdapters[ValuePathsToTypePaths[K]] ? never : K]: FormFieldOfFieldAdapter<FieldAdapters[ValuePathsToTypePaths[K]]>;
|
|
6
|
+
};
|
|
7
|
+
type FormFieldOfFieldAdapter<F extends FieldAdapter | undefined> = F extends FieldAdapter<infer _From, infer To, infer E> ? Field<To, E> : never;
|
|
8
|
+
export {};
|
|
@@ -1,61 +1,58 @@
|
|
|
1
1
|
import { type ElementOfArray, type Maybe } from '@strictly/base';
|
|
2
|
-
import { type Accessor, type
|
|
2
|
+
import { type Accessor, type FlattenedValuesOfType, type MobxValueOfType, type ReadonlyTypeOfType, type Type, type ValueOfType } from '@strictly/define';
|
|
3
3
|
import { type ReadonlyDeep, type SimplifyDeep, type StringKeyOf, type ValueOf } from 'type-fest';
|
|
4
4
|
import { type Field } from 'types/field';
|
|
5
|
-
import { type
|
|
6
|
-
import { type
|
|
5
|
+
import { type ErrorOfFieldAdapter, type FieldAdapter, type ToOfFieldAdapter } from './field_adapter';
|
|
6
|
+
import { type FlattenedListTypesOfType } from './flattened_list_types_of_type';
|
|
7
7
|
export type FlattenedConvertedFieldsOf<ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>> = {
|
|
8
|
-
readonly [K in keyof ValuePathsToAdapters]: Field<
|
|
8
|
+
readonly [K in keyof ValuePathsToAdapters]: Field<ToOfFieldAdapter<ValuePathsToAdapters[K]>, ErrorOfFieldAdapter<ValuePathsToAdapters[K]>>;
|
|
9
9
|
};
|
|
10
10
|
export type FlattenedTypePathsToAdaptersOf<FlattenedValues extends Readonly<Record<string, any>>, Context> = {
|
|
11
11
|
readonly [K in keyof FlattenedValues]?: FieldAdapter<ReadonlyDeep<FlattenedValues[K]>, any, any, any, Context>;
|
|
12
12
|
};
|
|
13
|
-
type FieldOverride<V = any> =
|
|
14
|
-
value: V;
|
|
15
|
-
};
|
|
13
|
+
type FieldOverride<V = any> = Maybe<V>;
|
|
16
14
|
type FlattenedFieldOverrides<ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>> = {
|
|
17
|
-
-readonly [K in keyof ValuePathsToAdapters]?: FieldOverride<
|
|
15
|
+
-readonly [K in keyof ValuePathsToAdapters]?: FieldOverride<ToOfFieldAdapter<ValuePathsToAdapters[K]>>;
|
|
18
16
|
};
|
|
19
17
|
type FlattenedErrors<ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>> = {
|
|
20
|
-
-readonly [K in keyof ValuePathsToAdapters]?:
|
|
18
|
+
-readonly [K in keyof ValuePathsToAdapters]?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>;
|
|
21
19
|
};
|
|
22
20
|
export type ValuePathsToAdaptersOf<TypePathsToAdapters extends Partial<Readonly<Record<string, FieldAdapter>>>, ValuePathsToTypePaths extends Readonly<Record<string, string>>> = keyof TypePathsToAdapters extends ValueOf<ValuePathsToTypePaths> ? {
|
|
23
21
|
readonly [K in keyof ValuePathsToTypePaths as unknown extends TypePathsToAdapters[ValuePathsToTypePaths[K]] ? never : K]: NonNullable<TypePathsToAdapters[ValuePathsToTypePaths[K]]>;
|
|
24
22
|
} : never;
|
|
25
|
-
export declare class FormPresenter<T extends Type, ValueToTypePaths extends Readonly<Record<string, string>>, TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<
|
|
26
|
-
readonly
|
|
23
|
+
export declare class FormPresenter<T extends Type, ValueToTypePaths extends Readonly<Record<string, string>>, TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<FlattenedValuesOfType<T, '*'>, ValueOfType<ReadonlyTypeOfType<T>>>, ValuePathsToAdapters extends ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths> = ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths>> {
|
|
24
|
+
readonly type: T;
|
|
27
25
|
private readonly adapters;
|
|
28
|
-
constructor(
|
|
26
|
+
constructor(type: T, adapters: TypePathsToAdapters);
|
|
29
27
|
private maybeGetAdapterForValuePath;
|
|
30
28
|
private getAdapterForValuePath;
|
|
31
29
|
typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K];
|
|
32
|
-
setFieldValueAndValidate<K extends keyof ValuePathsToAdapters>(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, valuePath: K, value:
|
|
33
|
-
setFieldValue<K extends keyof ValuePathsToAdapters>(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, valuePath: K, value:
|
|
34
|
-
addListItem<K extends keyof
|
|
35
|
-
removeListItem<K extends keyof
|
|
30
|
+
setFieldValueAndValidate<K extends keyof ValuePathsToAdapters>(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>): boolean;
|
|
31
|
+
setFieldValue<K extends keyof ValuePathsToAdapters>(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>): boolean;
|
|
32
|
+
addListItem<K extends keyof FlattenedListTypesOfType<T>>(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, valuePath: K, elementValue?: Maybe<ElementOfArray<FlattenedValuesOfType<T>[K]>>, index?: number): void;
|
|
33
|
+
removeListItem<K extends keyof FlattenedListTypesOfType<T>>(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, elementValuePath: `${K}.${number}`): void;
|
|
36
34
|
private internalSetFieldValue;
|
|
37
35
|
clearFieldError<K extends keyof ValuePathsToAdapters>(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, valuePath: K): void;
|
|
38
36
|
clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, valuePath: K): void;
|
|
39
|
-
clearAll(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, value:
|
|
37
|
+
clearAll(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, value: ValueOfType<T>): void;
|
|
38
|
+
isValuePathActive<K extends keyof ValuePathsToAdapters>(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, valuePath: K): boolean;
|
|
40
39
|
validateField<K extends keyof ValuePathsToAdapters>(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>, valuePath: K): boolean;
|
|
41
40
|
validateAll(model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>): boolean;
|
|
42
|
-
createModel(value:
|
|
41
|
+
createModel(value: ValueOfType<ReadonlyTypeOfType<T>>): FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>;
|
|
43
42
|
}
|
|
44
|
-
export declare class FormModel<T extends Type, ValueToTypePaths extends Readonly<Record<string, string>>, TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<
|
|
45
|
-
private readonly
|
|
43
|
+
export declare class FormModel<T extends Type, ValueToTypePaths extends Readonly<Record<string, string>>, TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<FlattenedValuesOfType<T, '*'>, ValueOfType<ReadonlyTypeOfType<T>>>, ValuePathsToAdapters extends ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths> = ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths>> {
|
|
44
|
+
private readonly type;
|
|
46
45
|
private readonly adapters;
|
|
47
|
-
accessor value:
|
|
46
|
+
accessor value: MobxValueOfType<T>;
|
|
48
47
|
accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>;
|
|
49
48
|
accessor errors: FlattenedErrors<ValuePathsToAdapters>;
|
|
50
49
|
private readonly flattenedTypeDefs;
|
|
51
|
-
constructor(
|
|
50
|
+
constructor(type: T, value: ValueOfType<ReadonlyTypeOfType<T>>, adapters: TypePathsToAdapters);
|
|
52
51
|
get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>;
|
|
53
52
|
private get knownFields();
|
|
54
53
|
private maybeSynthesizeFieldByValuePath;
|
|
55
54
|
private synthesizeFieldByPaths;
|
|
56
55
|
getAccessorForValuePath(valuePath: keyof ValuePathsToAdapters): Accessor | undefined;
|
|
57
56
|
get accessors(): Readonly<Record<string, Accessor>>;
|
|
58
|
-
protected isDisabled(_valuePath: keyof ValuePathsToAdapters): boolean;
|
|
59
|
-
protected isRequired(_valuePath: keyof ValuePathsToAdapters): boolean;
|
|
60
57
|
}
|
|
61
58
|
export {};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { assertExists, assertExistsAndReturn, assertState, checkValidNumber, toArray, UnreachableError, } from '@strictly/base';
|
|
2
|
-
import {
|
|
1
|
+
import { assertExists, assertExistsAndReturn, assertState, checkValidNumber, map, toArray, UnreachableError, } from '@strictly/base';
|
|
2
|
+
import { flattenAccessorsOfType, flattenTypesOfType, flattenValuesOfType, flattenValueTo, jsonPathPop, mobxCopy, valuePathToTypePath, } from '@strictly/define';
|
|
3
3
|
import { computed, observable, runInAction, } from 'mobx';
|
|
4
|
-
import {
|
|
4
|
+
import { UnreliableFieldConversionType, } from 'types/field_converters';
|
|
5
5
|
export class FormPresenter {
|
|
6
|
-
|
|
6
|
+
type;
|
|
7
7
|
adapters;
|
|
8
|
-
constructor(
|
|
9
|
-
this.
|
|
8
|
+
constructor(type, adapters) {
|
|
9
|
+
this.type = type;
|
|
10
10
|
this.adapters = adapters;
|
|
11
11
|
}
|
|
12
12
|
maybeGetAdapterForValuePath(valuePath) {
|
|
13
13
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
14
|
-
const typePath = valuePathToTypePath(this.
|
|
14
|
+
const typePath = valuePathToTypePath(this.type, valuePath, true);
|
|
15
15
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
16
16
|
return this.adapters[typePath];
|
|
17
17
|
}
|
|
@@ -19,7 +19,7 @@ export class FormPresenter {
|
|
|
19
19
|
return assertExistsAndReturn(this.maybeGetAdapterForValuePath(valuePath), 'expected adapter to be defined {}', valuePath);
|
|
20
20
|
}
|
|
21
21
|
typePath(valuePath) {
|
|
22
|
-
return valuePathToTypePath(this.
|
|
22
|
+
return valuePathToTypePath(this.type, valuePath, true);
|
|
23
23
|
}
|
|
24
24
|
setFieldValueAndValidate(model, valuePath, value) {
|
|
25
25
|
return this.internalSetFieldValue(model, valuePath, value, true);
|
|
@@ -27,7 +27,9 @@ export class FormPresenter {
|
|
|
27
27
|
setFieldValue(model, valuePath, value) {
|
|
28
28
|
return this.internalSetFieldValue(model, valuePath, value, false);
|
|
29
29
|
}
|
|
30
|
-
addListItem(model, valuePath,
|
|
30
|
+
addListItem(model, valuePath,
|
|
31
|
+
// TODO can this type be simplified?
|
|
32
|
+
elementValue = null, index) {
|
|
31
33
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
32
34
|
const listValuePath = valuePath;
|
|
33
35
|
const accessor = model.accessors[valuePath];
|
|
@@ -151,11 +153,9 @@ export class FormPresenter {
|
|
|
151
153
|
const conversion = revert(value, valuePath, model.value);
|
|
152
154
|
const accessor = model.getAccessorForValuePath(valuePath);
|
|
153
155
|
return runInAction(() => {
|
|
154
|
-
model.fieldOverrides[valuePath] =
|
|
155
|
-
value,
|
|
156
|
-
};
|
|
156
|
+
model.fieldOverrides[valuePath] = [value];
|
|
157
157
|
switch (conversion.type) {
|
|
158
|
-
case
|
|
158
|
+
case UnreliableFieldConversionType.Failure:
|
|
159
159
|
if (displayValidation) {
|
|
160
160
|
model.errors[valuePath] = conversion.error;
|
|
161
161
|
}
|
|
@@ -163,7 +163,7 @@ export class FormPresenter {
|
|
|
163
163
|
accessor.set(conversion.value[0]);
|
|
164
164
|
}
|
|
165
165
|
return false;
|
|
166
|
-
case
|
|
166
|
+
case UnreliableFieldConversionType.Success:
|
|
167
167
|
delete model.errors[valuePath];
|
|
168
168
|
accessor?.set(conversion.value);
|
|
169
169
|
return true;
|
|
@@ -190,12 +190,11 @@ export class FormPresenter {
|
|
|
190
190
|
const { convert, create, } = adapter;
|
|
191
191
|
const accessor = model.accessors[valuePath];
|
|
192
192
|
const value = accessor == null ? create(valuePath, model.value) : accessor.value;
|
|
193
|
-
const displayValue = convert(value, valuePath, model.value);
|
|
193
|
+
const { value: displayValue, } = convert(value, valuePath, model.value);
|
|
194
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
195
|
+
const key = valuePath;
|
|
194
196
|
runInAction(function () {
|
|
195
|
-
|
|
196
|
-
model.fieldOverrides[valuePath] = {
|
|
197
|
-
value: displayValue,
|
|
198
|
-
};
|
|
197
|
+
model.fieldOverrides[key] = [displayValue];
|
|
199
198
|
});
|
|
200
199
|
}
|
|
201
200
|
clearAll(model, value) {
|
|
@@ -203,21 +202,27 @@ export class FormPresenter {
|
|
|
203
202
|
model.errors = {};
|
|
204
203
|
// TODO this isn't correct, should reload from value
|
|
205
204
|
model.fieldOverrides = {};
|
|
206
|
-
model.value = mobxCopy(this.
|
|
205
|
+
model.value = mobxCopy(this.type, value);
|
|
207
206
|
});
|
|
208
207
|
}
|
|
208
|
+
isValuePathActive(model, valuePath) {
|
|
209
|
+
const values = flattenValuesOfType(this.type, model.value);
|
|
210
|
+
const keys = new Set(Object.keys(values));
|
|
211
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
212
|
+
return keys.has(valuePath);
|
|
213
|
+
}
|
|
209
214
|
validateField(model, valuePath) {
|
|
210
215
|
const { convert, revert, create, } = this.getAdapterForValuePath(valuePath);
|
|
211
216
|
const fieldOverride = model.fieldOverrides[valuePath];
|
|
212
217
|
const accessor = model.getAccessorForValuePath(valuePath);
|
|
213
|
-
const storedValue = convert(accessor != null
|
|
218
|
+
const { value: storedValue, } = convert(accessor != null
|
|
214
219
|
? accessor.value
|
|
215
220
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
216
221
|
: create(valuePath, model.value),
|
|
217
222
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
218
223
|
valuePath, model.value);
|
|
219
224
|
const value = fieldOverride != null
|
|
220
|
-
? fieldOverride
|
|
225
|
+
? fieldOverride[0]
|
|
221
226
|
: storedValue;
|
|
222
227
|
const dirty = storedValue !== value;
|
|
223
228
|
assertExists(revert, 'changing field directly not supported {}', valuePath);
|
|
@@ -225,13 +230,13 @@ export class FormPresenter {
|
|
|
225
230
|
const conversion = revert(value, valuePath, model.value);
|
|
226
231
|
return runInAction(function () {
|
|
227
232
|
switch (conversion.type) {
|
|
228
|
-
case
|
|
233
|
+
case UnreliableFieldConversionType.Failure:
|
|
229
234
|
model.errors[valuePath] = conversion.error;
|
|
230
235
|
if (conversion.value != null && accessor != null && dirty) {
|
|
231
236
|
accessor.set(conversion.value[0]);
|
|
232
237
|
}
|
|
233
238
|
return false;
|
|
234
|
-
case
|
|
239
|
+
case UnreliableFieldConversionType.Success:
|
|
235
240
|
delete model.errors[valuePath];
|
|
236
241
|
if (accessor != null && dirty) {
|
|
237
242
|
accessor.set(conversion.value);
|
|
@@ -262,21 +267,21 @@ export class FormPresenter {
|
|
|
262
267
|
return success;
|
|
263
268
|
}
|
|
264
269
|
const fieldOverride = model.fieldOverrides[adapterPath];
|
|
265
|
-
const storedValue = convert(accessor.value, valuePath, model.value);
|
|
270
|
+
const { value: storedValue, } = convert(accessor.value, valuePath, model.value);
|
|
266
271
|
const value = fieldOverride != null
|
|
267
|
-
? fieldOverride
|
|
272
|
+
? fieldOverride[0]
|
|
268
273
|
: storedValue;
|
|
269
274
|
// TODO more nuanced comparison
|
|
270
|
-
const dirty = fieldOverride != null && fieldOverride
|
|
275
|
+
const dirty = fieldOverride != null && fieldOverride[0] !== storedValue;
|
|
271
276
|
const conversion = revert(value, valuePath, model.value);
|
|
272
277
|
switch (conversion.type) {
|
|
273
|
-
case
|
|
278
|
+
case UnreliableFieldConversionType.Failure:
|
|
274
279
|
model.errors[adapterPath] = conversion.error;
|
|
275
280
|
if (conversion.value != null && dirty) {
|
|
276
281
|
accessor.set(conversion.value[0]);
|
|
277
282
|
}
|
|
278
283
|
return false;
|
|
279
|
-
case
|
|
284
|
+
case UnreliableFieldConversionType.Success:
|
|
280
285
|
if (dirty) {
|
|
281
286
|
accessor.set(conversion.value);
|
|
282
287
|
}
|
|
@@ -289,11 +294,11 @@ export class FormPresenter {
|
|
|
289
294
|
});
|
|
290
295
|
}
|
|
291
296
|
createModel(value) {
|
|
292
|
-
return new FormModel(this.
|
|
297
|
+
return new FormModel(this.type, value, this.adapters);
|
|
293
298
|
}
|
|
294
299
|
}
|
|
295
300
|
export class FormModel {
|
|
296
|
-
|
|
301
|
+
type;
|
|
297
302
|
adapters;
|
|
298
303
|
@observable.ref
|
|
299
304
|
accessor value;
|
|
@@ -302,14 +307,14 @@ export class FormModel {
|
|
|
302
307
|
@observable.shallow
|
|
303
308
|
accessor errors = {};
|
|
304
309
|
flattenedTypeDefs;
|
|
305
|
-
constructor(
|
|
306
|
-
this.
|
|
310
|
+
constructor(type, value, adapters) {
|
|
311
|
+
this.type = type;
|
|
307
312
|
this.adapters = adapters;
|
|
308
|
-
this.value = mobxCopy(
|
|
309
|
-
this.flattenedTypeDefs =
|
|
313
|
+
this.value = mobxCopy(type, value);
|
|
314
|
+
this.flattenedTypeDefs = flattenTypesOfType(type);
|
|
310
315
|
// pre-populate field overrides for consistent behavior when default information is overwritten
|
|
311
316
|
// then returned to
|
|
312
|
-
|
|
317
|
+
const conversions = flattenValueTo(type, this.value, () => { }, (_t, value, _setter, typePath, valuePath) => {
|
|
313
318
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
314
319
|
const adapter = this.adapters[typePath];
|
|
315
320
|
if (adapter == null) {
|
|
@@ -320,10 +325,11 @@ export class FormModel {
|
|
|
320
325
|
// no need to store a temporary value if the value cannot be written back
|
|
321
326
|
return;
|
|
322
327
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
328
|
+
return convert(value, valuePath, this.value);
|
|
329
|
+
});
|
|
330
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
331
|
+
this.fieldOverrides = map(conversions, function (_k, v) {
|
|
332
|
+
return v && [v.value];
|
|
327
333
|
});
|
|
328
334
|
}
|
|
329
335
|
@computed
|
|
@@ -344,7 +350,7 @@ export class FormModel {
|
|
|
344
350
|
}
|
|
345
351
|
@computed
|
|
346
352
|
get knownFields() {
|
|
347
|
-
return
|
|
353
|
+
return flattenValueTo(this.type, this.value, () => { },
|
|
348
354
|
// TODO swap these to valuePath, typePath in flatten
|
|
349
355
|
(_t, _v, _setter, typePath, valuePath) => {
|
|
350
356
|
return this.synthesizeFieldByPaths(
|
|
@@ -358,7 +364,7 @@ export class FormModel {
|
|
|
358
364
|
let typePath;
|
|
359
365
|
try {
|
|
360
366
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
361
|
-
typePath = valuePathToTypePath(this.
|
|
367
|
+
typePath = valuePathToTypePath(this.type,
|
|
362
368
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
363
369
|
valuePath, true);
|
|
364
370
|
}
|
|
@@ -381,26 +387,23 @@ export class FormModel {
|
|
|
381
387
|
const accessor = this.getAccessorForValuePath(valuePath);
|
|
382
388
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
383
389
|
const fieldTypeDef = this.flattenedTypeDefs[typePath];
|
|
384
|
-
const value =
|
|
385
|
-
?
|
|
386
|
-
:
|
|
387
|
-
?
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
396
|
-
valuePath, this.value);
|
|
390
|
+
const { value, required, readonly, } = convert(accessor != null
|
|
391
|
+
? accessor.value
|
|
392
|
+
: fieldTypeDef != null
|
|
393
|
+
? mobxCopy(fieldTypeDef,
|
|
394
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
395
|
+
create(valuePath, this.value))
|
|
396
|
+
// fake values can't be copied
|
|
397
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
398
|
+
: create(valuePath, this.value),
|
|
399
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
400
|
+
valuePath, this.value);
|
|
397
401
|
const error = this.errors[valuePath];
|
|
398
402
|
return {
|
|
399
|
-
value,
|
|
403
|
+
value: fieldOverride != null ? fieldOverride[0] : value,
|
|
400
404
|
error,
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
required: this.isRequired(valuePath),
|
|
405
|
+
readonly,
|
|
406
|
+
required,
|
|
404
407
|
};
|
|
405
408
|
}
|
|
406
409
|
getAccessorForValuePath(valuePath) {
|
|
@@ -410,16 +413,8 @@ export class FormModel {
|
|
|
410
413
|
@computed
|
|
411
414
|
// should only be referenced internally, so loosely typed
|
|
412
415
|
get accessors() {
|
|
413
|
-
return
|
|
414
|
-
this.value = mobxCopy(this.
|
|
416
|
+
return flattenAccessorsOfType(this.type, this.value, (value) => {
|
|
417
|
+
this.value = mobxCopy(this.type, value);
|
|
415
418
|
});
|
|
416
419
|
}
|
|
417
|
-
isDisabled(_valuePath) {
|
|
418
|
-
// TODO infer from types
|
|
419
|
-
return false;
|
|
420
|
-
}
|
|
421
|
-
isRequired(_valuePath) {
|
|
422
|
-
// TODO infer from types
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
420
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type TwoWayFieldConverter } from 'types/field_converters';
|
|
2
|
+
import { type ErrorOfFieldAdapter, type FieldAdapter, type FromOfFieldAdapter, type ToOfFieldAdapter, type ValuePathOfFieldAdapter } from './field_adapter';
|
|
3
|
+
export type MergedOfFieldAdaptersWithTwoWayConverter<FieldAdapters extends Readonly<Record<string, FieldAdapter>>, E, Context> = {
|
|
4
|
+
[K in keyof FieldAdapters]: FieldAdapter<FromOfFieldAdapter<FieldAdapters[K]>, ToOfFieldAdapter<FieldAdapters[K]>, ErrorOfFieldAdapter<FieldAdapters[K]> | E, ValuePathOfFieldAdapter<FieldAdapters[K]>, Context>;
|
|
5
|
+
};
|
|
6
|
+
type ValuePathsOfFieldAdapters<FieldAdapters extends Readonly<Record<string, FieldAdapter>>> = {
|
|
7
|
+
[K in keyof FieldAdapters]: ValuePathOfFieldAdapter<FieldAdapters[K]>;
|
|
8
|
+
}[keyof FieldAdapters];
|
|
9
|
+
type TosOfFieldAdapters<FieldAdapters extends Readonly<Record<string, FieldAdapter>>> = {
|
|
10
|
+
[K in keyof FieldAdapters]: ToOfFieldAdapter<FieldAdapters[K]>;
|
|
11
|
+
}[keyof FieldAdapters];
|
|
12
|
+
export declare function mergeFieldAdaptersWithTwoWayConverter<FieldAdapters extends Readonly<Record<string, FieldAdapter>>, E, Context>(fieldAdapters: FieldAdapters, converter: TwoWayFieldConverter<TosOfFieldAdapters<FieldAdapters>, TosOfFieldAdapters<FieldAdapters>, E, ValuePathsOfFieldAdapters<FieldAdapters>, Context>): MergedOfFieldAdaptersWithTwoWayConverter<FieldAdapters, E, Context>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { map } from '@strictly/base';
|
|
2
|
+
import { chainAnnotatedFieldConverter, chainUnreliableFieldConverter, } from 'field_converters/chain_field_converter';
|
|
3
|
+
export function mergeFieldAdaptersWithTwoWayConverter(fieldAdapters, converter) {
|
|
4
|
+
return map(fieldAdapters, function (_key, adapter) {
|
|
5
|
+
return {
|
|
6
|
+
convert: chainAnnotatedFieldConverter(adapter.convert.bind(adapter), converter.convert.bind(converter)),
|
|
7
|
+
revert: adapter.revert && chainUnreliableFieldConverter(converter.revert.bind(converter), adapter.revert.bind(adapter)),
|
|
8
|
+
create: adapter.create.bind(adapter),
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Validator } from '@strictly/define';
|
|
2
|
+
import { type Simplify } from 'type-fest';
|
|
3
|
+
import { type FieldAdapter } from './field_adapter';
|
|
4
|
+
export type MergedOfFieldAdaptersWithValidators<FieldAdapters extends Readonly<Record<Key, FieldAdapter>>, Validators extends Partial<Readonly<Record<string, Validator>>>, Key extends keyof Validators = keyof Validators> = Simplify<{
|
|
5
|
+
readonly [K in Key]: MergedOfFieldAdapterWithValidator<FieldAdapters[K], Validators[K]>;
|
|
6
|
+
} & {
|
|
7
|
+
readonly [K in Exclude<keyof FieldAdapters, Key>]: FieldAdapters[K];
|
|
8
|
+
}>;
|
|
9
|
+
type MergedOfFieldAdapterWithValidator<A extends FieldAdapter, V extends Validator | undefined> = undefined extends V ? A : A extends FieldAdapter<infer From, infer To, infer E1, infer P1, infer C1> ? V extends Validator<From, infer E2, infer P2, infer C2> ? FieldAdapter<From, To, E1 | E2, P1 | P2, C1 | C2> : never : never;
|
|
10
|
+
export declare function mergeAdaptersWithValidators<FieldAdapters extends Readonly<Record<Key, FieldAdapter>>, Validators extends Readonly<Record<string, Validator>>, Key extends keyof Validators = keyof Validators>(adapters: FieldAdapters, validators: Validators): MergedOfFieldAdaptersWithValidators<FieldAdapters, Validators, Key>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { reduce } from '@strictly/base';
|
|
2
|
+
import { annotations, validate, } from '@strictly/define';
|
|
3
|
+
import { UnreliableFieldConversionType, } from 'types/field_converters';
|
|
4
|
+
export function mergeAdaptersWithValidators(adapters, validators) {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
6
|
+
return reduce(adapters, function (acc, key, adapter) {
|
|
7
|
+
const validator = validators[key];
|
|
8
|
+
if (validator == null) {
|
|
9
|
+
acc[key] = adapter;
|
|
10
|
+
return acc;
|
|
11
|
+
}
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
function revert(to, ...params) {
|
|
14
|
+
const result = adapter.revert(to, ...params);
|
|
15
|
+
if (result.type === UnreliableFieldConversionType.Failure) {
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
const validationError = validate(validator, result.value, ...params);
|
|
19
|
+
if (validationError == null) {
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
type: UnreliableFieldConversionType.Failure,
|
|
24
|
+
value: [result.value],
|
|
25
|
+
error: validationError,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
function convert(from, ...params) {
|
|
30
|
+
const { required: required1, readonly: readonly1, value, } = adapter.convert(from, ...params);
|
|
31
|
+
const { required: required2, readonly: readonly2, } = annotations(validator, ...params);
|
|
32
|
+
return {
|
|
33
|
+
value,
|
|
34
|
+
required: required1 || required2,
|
|
35
|
+
readonly: readonly1 || readonly2,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
acc[key] = {
|
|
39
|
+
...adapter,
|
|
40
|
+
convert,
|
|
41
|
+
revert: adapter.revert && revert,
|
|
42
|
+
};
|
|
43
|
+
return acc;
|
|
44
|
+
}, {});
|
|
45
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type FieldAdapter } from 'core/mobx/field_adapter';
|
|
2
|
+
import { type TwoWayFieldConverter } from 'types/field_converters';
|
|
3
|
+
import { type Mocked } from 'vitest';
|
|
4
|
+
export declare function createMockedAdapter<E, To, From, ValuePath extends string>(_original: FieldAdapter<From, To, E, ValuePath>): Mocked<Required<FieldAdapter<From, To, E, ValuePath>>>;
|
|
5
|
+
export declare function resetMockAdapter<E, To, From, ValuePath extends string>({ convert, revert, create, }: FieldAdapter<From, To, E, ValuePath>, mockedAdapter: Mocked<Required<FieldAdapter<From, To, E, ValuePath>>>): void;
|
|
6
|
+
export declare function createMockTwoWayFieldConverter<From, To, E, ValuePath extends string, Context>(_original: TwoWayFieldConverter<From, To, E, ValuePath, Context>): Mocked<TwoWayFieldConverter<From, To, E, ValuePath, Context>>;
|
|
7
|
+
export declare function resetMockTwoWayFieldConverter<From, To, E, ValuePath extends string, Context>({ convert, revert, }: TwoWayFieldConverter<From, To, E, ValuePath, Context>, mockedConverter: Mocked<TwoWayFieldConverter<From, To, E, ValuePath, Context>>): void;
|