@strictly/react-form 0.0.7 → 0.0.9

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 (79) hide show
  1. package/.out/core/mobx/field_adapter_builder.d.ts +4 -0
  2. package/.out/core/mobx/field_adapter_builder.js +31 -0
  3. package/.out/core/mobx/hooks.d.ts +5 -1
  4. package/.out/core/mobx/hooks.js +13 -3
  5. package/.out/core/mobx/specs/sub_form_field_adapters.tests.js +56 -9
  6. package/.out/core/mobx/sub_form_field_adapters.d.ts +5 -5
  7. package/.out/core/mobx/sub_form_field_adapters.js +13 -10
  8. package/.out/field_converters/chain_field_converter.js +3 -3
  9. package/.out/field_converters/nullable_to_boolean_converter.d.ts +2 -2
  10. package/.out/mantine/create_checkbox.js +1 -0
  11. package/.out/mantine/create_fields_view.d.ts +1 -1
  12. package/.out/mantine/create_fields_view.js +4 -4
  13. package/.out/mantine/create_form.d.ts +1 -1
  14. package/.out/mantine/create_list.d.ts +1 -1
  15. package/.out/mantine/create_pill.d.ts +1 -1
  16. package/.out/mantine/create_radio.d.ts +1 -1
  17. package/.out/mantine/create_radio_group.js +1 -0
  18. package/.out/mantine/create_text_input.js +7 -2
  19. package/.out/mantine/create_value_input.js +1 -0
  20. package/.out/mantine/error_renderer.d.ts +7 -3
  21. package/.out/mantine/error_renderer.js +1 -1
  22. package/.out/mantine/hooks.d.ts +9 -9
  23. package/.out/mantine/specs/checkbox_hooks.stories.d.ts +2 -6
  24. package/.out/mantine/specs/checkbox_hooks.stories.js +4 -16
  25. package/.out/mantine/specs/fields_view_hooks.stories.d.ts +1 -1
  26. package/.out/mantine/specs/fields_view_hooks.stories.js +6 -3
  27. package/.out/mantine/specs/form_hooks.stories.d.ts +2 -2
  28. package/.out/mantine/specs/form_hooks.stories.js +4 -1
  29. package/.out/mantine/specs/radio_group_hooks.stories.d.ts +2 -6
  30. package/.out/mantine/specs/radio_group_hooks.stories.js +5 -17
  31. package/.out/mantine/specs/select_hooks.stories.d.ts +2 -6
  32. package/.out/mantine/specs/select_hooks.stories.js +4 -16
  33. package/.out/mantine/specs/text_input_hooks.stories.d.ts +2 -5
  34. package/.out/mantine/specs/text_input_hooks.stories.js +5 -5
  35. package/.out/mantine/specs/value_input_hooks.stories.d.ts +2 -5
  36. package/.out/mantine/specs/value_input_hooks.stories.js +5 -5
  37. package/.out/mantine/types.d.ts +4 -2
  38. package/.out/tsconfig.tsbuildinfo +1 -1
  39. package/.out/types/specs/{error_type_of_field.tests.js → error_of_field.tests.js} +1 -1
  40. package/.turbo/turbo-build.log +8 -8
  41. package/.turbo/turbo-check-types.log +1 -1
  42. package/core/mobx/field_adapter_builder.ts +71 -0
  43. package/core/mobx/hooks.ts +24 -6
  44. package/core/mobx/specs/sub_form_field_adapters.tests.ts +108 -15
  45. package/core/mobx/sub_form_field_adapters.ts +41 -25
  46. package/dist/index.cjs +108 -49
  47. package/dist/index.d.cts +35 -24
  48. package/dist/index.d.ts +35 -24
  49. package/dist/index.js +107 -49
  50. package/field_converters/chain_field_converter.ts +3 -3
  51. package/field_converters/nullable_to_boolean_converter.ts +2 -3
  52. package/mantine/create_checkbox.tsx +2 -1
  53. package/mantine/create_fields_view.tsx +17 -14
  54. package/mantine/create_form.tsx +2 -2
  55. package/mantine/create_list.tsx +1 -1
  56. package/mantine/create_pill.tsx +1 -1
  57. package/mantine/create_radio.tsx +1 -1
  58. package/mantine/create_radio_group.tsx +6 -2
  59. package/mantine/create_text_input.tsx +9 -3
  60. package/mantine/create_value_input.tsx +2 -1
  61. package/mantine/error_renderer.ts +13 -4
  62. package/mantine/hooks.tsx +19 -14
  63. package/mantine/specs/__snapshots__/checkbox_hooks.tests.tsx.snap +1 -64
  64. package/mantine/specs/__snapshots__/fields_view_hooks.tests.tsx.snap +52 -52
  65. package/mantine/specs/__snapshots__/radio_group_hooks.tests.tsx.snap +1 -179
  66. package/mantine/specs/__snapshots__/select_hooks.tests.tsx.snap +1 -83
  67. package/mantine/specs/__snapshots__/text_input_hooks.tests.tsx.snap +27 -27
  68. package/mantine/specs/__snapshots__/value_input_hooks.tests.tsx.snap +31 -31
  69. package/mantine/specs/checkbox_hooks.stories.tsx +5 -21
  70. package/mantine/specs/fields_view_hooks.stories.tsx +16 -4
  71. package/mantine/specs/form_hooks.stories.tsx +10 -3
  72. package/mantine/specs/radio_group_hooks.stories.tsx +6 -20
  73. package/mantine/specs/select_hooks.stories.tsx +5 -21
  74. package/mantine/specs/text_input_hooks.stories.tsx +5 -8
  75. package/mantine/specs/value_input_hooks.stories.tsx +5 -8
  76. package/mantine/types.ts +7 -3
  77. package/package.json +2 -1
  78. package/types/specs/{error_type_of_field.tests.ts → error_of_field.tests.ts} +1 -1
  79. /package/.out/types/specs/{error_type_of_field.tests.d.ts → error_of_field.tests.d.ts} +0 -0
@@ -7,6 +7,9 @@ declare class FieldAdapterBuilder<From, To, E, ValuePath extends string, Context
7
7
  constructor(convert: AnnotatedFieldConverter<From, To, ValuePath, Context>, create: FieldValueFactory<From, ValuePath, Context>, revert?: UnreliableFieldConverter<To, From, E, ValuePath, Context> | undefined);
8
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
9
  withReverter(reverter: UnreliableFieldConverter<To, From, E, ValuePath, Context>): FieldAdapterBuilder<From, To, E, ValuePath, Context>;
10
+ nullable(): FieldAdapterBuilder<From | null, To | null, E, ValuePath, Context>;
11
+ optional(): FieldAdapterBuilder<From | undefined, To | undefined, E, ValuePath, Context>;
12
+ private or;
10
13
  withIdentity(isFrom: (from: To | From) => from is From): FieldAdapterBuilder<From, To | From, E, ValuePath, Context>;
11
14
  get narrow(): FieldAdapter<From, To, E, ValuePath, Context>;
12
15
  }
@@ -17,5 +20,6 @@ export declare function adapterFromTwoWayConverter<From, To, E, ValuePath extend
17
20
  export declare function adapterFromPrototype<From, To, ValuePath extends string, Context>(converter: AnnotatedFieldConverter<From, To, ValuePath, Context>, prototype: From): FieldAdapterBuilder<From, To, never, ValuePath, Context>;
18
21
  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>;
19
22
  export declare function identityAdapter<V, ValuePath extends string, Context>(prototype: V, required?: boolean): FieldAdapterBuilder<V, V, never, ValuePath, Context>;
23
+ export declare function trimmingStringAdapter<ValuePath extends string, Context>(): FieldAdapterBuilder<string, string, never, ValuePath, Context>;
20
24
  export declare function listAdapter<E, ValuePath extends string, Context>(): FieldAdapterBuilder<readonly E[], readonly E[], never, ValuePath, Context>;
21
25
  export {};
@@ -1,7 +1,9 @@
1
1
  import { chainAnnotatedFieldConverter, chainUnreliableFieldConverter, } from 'field_converters/chain_field_converter';
2
2
  import { annotatedIdentityConverter, unreliableIdentityConverter, } from 'field_converters/identity_converter';
3
3
  import { MaybeIdentityConverter } from 'field_converters/maybe_identity_converter';
4
+ import { TrimmingStringConverter } from 'field_converters/trimming_string_converter';
4
5
  import { prototypingFieldValueFactory } from 'field_value_factories/prototyping_field_value_factory';
6
+ import { UnreliableFieldConversionType, } from 'types/field_converters';
5
7
  class FieldAdapterBuilder {
6
8
  convert;
7
9
  create;
@@ -17,6 +19,32 @@ class FieldAdapterBuilder {
17
19
  withReverter(reverter) {
18
20
  return new FieldAdapterBuilder(this.convert, this.create, reverter);
19
21
  }
22
+ nullable() {
23
+ return this.or(null);
24
+ }
25
+ optional() {
26
+ return this.or(undefined);
27
+ }
28
+ or(proto) {
29
+ function isFrom(v) {
30
+ return v !== proto;
31
+ }
32
+ function isTo(v) {
33
+ return v !== proto;
34
+ }
35
+ return new FieldAdapterBuilder((v, valuePath, context) => isFrom(v)
36
+ ? this.convert(v, valuePath, context)
37
+ : {
38
+ value: v,
39
+ readonly: false,
40
+ required: false,
41
+ }, this.create, (v, valuePath, context) => isTo(v) && this.revert
42
+ ? this.revert(v, valuePath, context)
43
+ : {
44
+ type: UnreliableFieldConversionType.Success,
45
+ value: proto,
46
+ });
47
+ }
20
48
  withIdentity(isFrom) {
21
49
  const identityConverter = new MaybeIdentityConverter({
22
50
  convert: this.convert,
@@ -46,6 +74,9 @@ export function adapterFromPrototype(converter, prototype) {
46
74
  export function identityAdapter(prototype, required) {
47
75
  return new FieldAdapterBuilder(annotatedIdentityConverter(required), prototypingFieldValueFactory(prototype), unreliableIdentityConverter());
48
76
  }
77
+ export function trimmingStringAdapter() {
78
+ return adapterFromTwoWayConverter(new TrimmingStringConverter(), prototypingFieldValueFactory(''));
79
+ }
49
80
  export function listAdapter() {
50
81
  return new FieldAdapterBuilder(annotatedIdentityConverter(false), prototypingFieldValueFactory([]), unreliableIdentityConverter());
51
82
  }
@@ -4,7 +4,11 @@ import { type FormPresenter } from './form_presenter';
4
4
  import { type ValuePathsOfPresenter } from './types';
5
5
  type ValueOfPresenter<P extends FormPresenter<any, any, any, any>> = P extends FormPresenter<infer T, any, any, any> ? ValueOfType<ReadonlyTypeOfType<T>> : never;
6
6
  type ModelOfPresenter<P extends FormPresenter<any, any, any, any>> = ReturnType<P['createModel']>;
7
- export declare function useDefaultMobxFormHooks<P extends FormPresenter<any, any, any, any>>(presenter: P, value: ValueOfPresenter<P>, onValidSubmit?: <Path extends ValuePathsOfPresenter<P>>(model: ModelOfPresenter<P>, valuePath: Path) => void): {
7
+ export declare function useDefaultMobxFormHooks<P extends FormPresenter<any, any, any, any>>(presenter: P, value: ValueOfPresenter<P>, { onValidFieldSubmit, onValidFormSubmit, }?: {
8
+ onValidFieldSubmit?: <Path extends ValuePathsOfPresenter<P>>(model: ModelOfPresenter<P>, valuePath: Path) => void;
9
+ onValidFormSubmit?: (model: ModelOfPresenter<P>, value: ValueOfPresenter<P>) => void;
10
+ }): {
8
11
  model: ModelOfPresenter<P>;
12
+ onFormSubmit?: () => void;
9
13
  } & Omit<FieldsViewProps<ModelOfPresenter<P>['fields']>, 'fields'>;
10
14
  export {};
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useMemo, } from 'react';
2
2
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
3
- export function useDefaultMobxFormHooks(presenter, value, onValidSubmit) {
3
+ export function useDefaultMobxFormHooks(presenter, value, { onValidFieldSubmit, onValidFormSubmit, } = {}) {
4
4
  const model = useMemo(function () {
5
5
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
6
6
  return presenter.createModel(value);
@@ -17,13 +17,13 @@ export function useDefaultMobxFormHooks(presenter, value, onValidSubmit) {
17
17
  ]);
18
18
  const onFieldSubmit = useCallback(function (valuePath) {
19
19
  if (presenter.validateField(model, valuePath)) {
20
- onValidSubmit?.(model, valuePath);
20
+ onValidFieldSubmit?.(model, valuePath);
21
21
  }
22
22
  return false;
23
23
  }, [
24
24
  presenter,
25
25
  model,
26
- onValidSubmit,
26
+ onValidFieldSubmit,
27
27
  ]);
28
28
  const onFieldBlur = useCallback(function (path) {
29
29
  // work around potential loss of focus prior to state potentially invalidating change triggering
@@ -38,10 +38,20 @@ export function useDefaultMobxFormHooks(presenter, value, onValidSubmit) {
38
38
  presenter,
39
39
  model,
40
40
  ]);
41
+ const onFormSubmit = useCallback(function () {
42
+ if (presenter.validateAll(model)) {
43
+ onValidFormSubmit?.(model, model.value);
44
+ }
45
+ }, [
46
+ presenter,
47
+ model,
48
+ onValidFormSubmit,
49
+ ]);
41
50
  return {
42
51
  model,
43
52
  onFieldValueChange,
44
53
  onFieldSubmit,
45
54
  onFieldBlur,
55
+ onFormSubmit,
46
56
  };
47
57
  }
@@ -1,12 +1,8 @@
1
- import { numberType, object, stringType, } from '@strictly/define';
1
+ import { list, numberType, object, stringType, } from '@strictly/define';
2
2
  import { subFormFieldAdapters, } from 'core/mobx/sub_form_field_adapters';
3
3
  import { UnreliableFieldConversionType } from 'types/field_converters';
4
- import { mockDeep } from 'vitest-mock-extended';
4
+ import { mockDeep, mockReset, } from 'vitest-mock-extended';
5
5
  describe('subFormFieldAdapters', () => {
6
- const mockedFieldAdapter1 = mockDeep();
7
- const fieldAdapter1 = mockedFieldAdapter1;
8
- const mockedFieldAdapter2 = mockDeep();
9
- const fieldAdapter2 = mockedFieldAdapter2;
10
6
  describe('empty value', () => {
11
7
  const adapters = subFormFieldAdapters({}, '$.a', stringType);
12
8
  it('equals expected type', () => {
@@ -17,12 +13,19 @@ describe('subFormFieldAdapters', () => {
17
13
  });
18
14
  });
19
15
  describe('single adapter', () => {
16
+ const mockedFieldAdapter1 = mockDeep();
17
+ const fieldAdapter1 = mockedFieldAdapter1;
20
18
  const type = object().field('a', stringType);
21
- const adapters = subFormFieldAdapters({
19
+ const subAdapters = {
22
20
  $: fieldAdapter1,
23
- }, '$.a', type);
21
+ };
22
+ const adapters = subFormFieldAdapters(subAdapters, '$.a', type);
23
+ beforeEach(() => {
24
+ mockReset(mockedFieldAdapter1);
25
+ });
24
26
  it('equals expected type', () => {
25
- // TODO toEqualTypeOf (seems to be a TS error)
27
+ // TODO toEqualTypeOf (cannot reason about revert optionality, seems to be a TS issue as they
28
+ // are both optional AFAICT)
26
29
  expectTypeOf(adapters).toMatchTypeOf();
27
30
  });
28
31
  it('equals expected value', () => {
@@ -58,12 +61,20 @@ describe('subFormFieldAdapters', () => {
58
61
  });
59
62
  });
60
63
  describe('multiple adapters', () => {
64
+ const mockedFieldAdapter1 = mockDeep();
65
+ const fieldAdapter1 = mockedFieldAdapter1;
66
+ const mockedFieldAdapter2 = mockDeep();
67
+ const fieldAdapter2 = mockedFieldAdapter2;
61
68
  const type = object()
62
69
  .field('a', object().field('x', stringType).field('y', numberType));
63
70
  const adapters = subFormFieldAdapters({
64
71
  '$.x': fieldAdapter1,
65
72
  '$.y': fieldAdapter2,
66
73
  }, '$.a', type);
74
+ beforeEach(() => {
75
+ mockReset(mockedFieldAdapter1);
76
+ mockReset(mockedFieldAdapter2);
77
+ });
67
78
  it('equals expected type', () => {
68
79
  // TODO toEqualTypeOf (seems to be a TS error)
69
80
  expectTypeOf(adapters).toMatchTypeOf();
@@ -106,4 +117,40 @@ describe('subFormFieldAdapters', () => {
106
117
  });
107
118
  });
108
119
  });
120
+ describe('list adapter', () => {
121
+ const mockedFieldAdapter1 = mockDeep();
122
+ const fieldAdapter1 = mockedFieldAdapter1;
123
+ const type = list(stringType);
124
+ const subAdapters = {
125
+ $: fieldAdapter1,
126
+ };
127
+ const adapters = subFormFieldAdapters(subAdapters, '$.*', type);
128
+ beforeEach(() => {
129
+ mockReset(mockedFieldAdapter1);
130
+ });
131
+ it('equals expected type', () => {
132
+ // TODO toEqualTypeOf (seems to be a TS error)
133
+ expectTypeOf(adapters).toMatchTypeOf();
134
+ });
135
+ it('equals expected value', () => {
136
+ expect(adapters).toEqual({
137
+ '$.*': expect.anything(),
138
+ });
139
+ });
140
+ describe('calls convert with correct paths and values', () => {
141
+ const subContext = 'a';
142
+ const context = [subContext];
143
+ it('calls $.*', () => {
144
+ const mockedReturnedValue = {
145
+ value: false,
146
+ readonly: false,
147
+ required: false,
148
+ };
149
+ mockedFieldAdapter1.convert.mockReturnValue(mockedReturnedValue);
150
+ const returnedValue = adapters['$.*'].convert('b', '$.0', context);
151
+ expect(fieldAdapter1.convert).toHaveBeenCalledWith('b', '$', subContext);
152
+ expect(returnedValue).toEqual(mockedReturnedValue);
153
+ });
154
+ });
155
+ });
109
156
  });
@@ -1,9 +1,9 @@
1
1
  import { type StringConcatOf } from '@strictly/base';
2
2
  import { type Type, type ValueOfType } from '@strictly/define';
3
- import { type ErrorOfFieldAdapter, type FieldAdapter, type FromOfFieldAdapter, type ToOfFieldAdapter } from './field_adapter';
4
- type SubFormFieldAdapter<F extends FieldAdapter, P extends string, Context> = FieldAdapter<FromOfFieldAdapter<F>, ToOfFieldAdapter<F>, ErrorOfFieldAdapter<F>, P, Context>;
5
- type SubFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>, P extends string, Context> = {
6
- [K in keyof SubAdapters as K extends StringConcatOf<'$', infer S> ? `${P}${S}` : never]: K extends StringConcatOf<'$', infer S> ? SubFormFieldAdapter<SubAdapters[K], `${P}${S}`, Context> : never;
3
+ import { type ErrorOfFieldAdapter, type FieldAdapter, type FromOfFieldAdapter, type ToOfFieldAdapter, type ValuePathOfFieldAdapter } from './field_adapter';
4
+ type SubFormFieldAdapter<F extends FieldAdapter, ValuePath extends string, Context> = FieldAdapter<FromOfFieldAdapter<F>, ToOfFieldAdapter<F>, ErrorOfFieldAdapter<F>, ValuePathOfFieldAdapter<F> extends StringConcatOf<'$', infer ValuePathSuffix> ? `${ValuePath}${ValuePathSuffix}` : string, Context>;
5
+ type SubFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>, TypePath extends string, ValuePath extends string, Context> = {
6
+ [K in keyof SubAdapters as K extends StringConcatOf<'$', infer TypePathSuffix> ? `${TypePath}${TypePathSuffix}` : never]: SubFormFieldAdapter<SubAdapters[K], ValuePath, Context>;
7
7
  };
8
- export declare function subFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>, P extends string, ContextType extends Type>(subAdapters: SubAdapters, prefix: P, contextType: ContextType): SubFormFieldAdapters<SubAdapters, P, ValueOfType<ContextType>>;
8
+ export declare function subFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>, TypePath extends string, TypePathsToValuePaths extends Record<TypePath, string>, ContextType extends Type>(subAdapters: SubAdapters, parentTypePath: TypePath, contextType: ContextType): SubFormFieldAdapters<SubAdapters, TypePath, TypePathsToValuePaths[TypePath], ValueOfType<ContextType>>;
9
9
  export {};
@@ -1,29 +1,32 @@
1
1
  import { flattenValuesOfType, } from '@strictly/define';
2
- export function subFormFieldAdapters(subAdapters, prefix, contextType) {
2
+ export function subFormFieldAdapters(subAdapters, parentTypePath, contextType) {
3
+ // assume the number of '.' in the type path will correspond to the number of '.' in the value path
4
+ const dotCount = parentTypePath.split('.').length;
3
5
  function getSubValuePathAndContext(valuePath, context) {
4
- const subValuePath = valuePath.replace(prefix, '$');
5
- const subContext = flattenValuesOfType(contextType, context)[prefix];
6
+ const parentValuePath = valuePath.split('.').slice(0, dotCount).join('.');
7
+ const subValuePath = valuePath.replace(parentValuePath, '$');
8
+ const subContext = flattenValuesOfType(contextType, context)[parentValuePath];
6
9
  return [
7
10
  subValuePath,
8
11
  subContext,
9
12
  ];
10
13
  }
11
14
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12
- return Object.entries(subAdapters).reduce((acc, [subKey, subValue,]) => {
13
- const key = subKey.replace('$', prefix);
15
+ return Object.entries(subAdapters).reduce((acc, [subTypePath, subAdapter,]) => {
16
+ const typePath = subTypePath.replace('$', parentTypePath);
14
17
  // adapt field adapter with new path and context
15
18
  const adaptedAdapter = {
16
19
  convert: (from, valuePath, context) => {
17
- return subValue.convert(from, ...getSubValuePathAndContext(valuePath, context));
20
+ return subAdapter.convert(from, ...getSubValuePathAndContext(valuePath, context));
18
21
  },
19
22
  create: (valuePath, context) => {
20
- return subValue.create(...getSubValuePathAndContext(valuePath, context));
23
+ return subAdapter.create(...getSubValuePathAndContext(valuePath, context));
21
24
  },
22
- revert: subValue.revert && ((from, valuePath, context) => {
23
- return subValue.revert(from, ...getSubValuePathAndContext(valuePath, context));
25
+ revert: subAdapter.revert && ((from, valuePath, context) => {
26
+ return subAdapter.revert(from, ...getSubValuePathAndContext(valuePath, context));
24
27
  }),
25
28
  };
26
- acc[key] = adaptedAdapter;
29
+ acc[typePath] = adaptedAdapter;
27
30
  return acc;
28
31
  }, {});
29
32
  }
@@ -40,12 +40,12 @@ export function chainUnreliableFieldConverter(from, to) {
40
40
  }
41
41
  export function chainAnnotatedFieldConverter(from, to) {
42
42
  return function (value, valuePath, context) {
43
- const { required: intermediateRequired, readonly: intermediateDisabled, value: intermediateValue, } = from(value, valuePath, context);
44
- const { required: finalRequired, readonly: finalDisabled, value: finalValue, } = to(intermediateValue, valuePath, context);
43
+ const { required: intermediateRequired, readonly: intermediateReadonly, value: intermediateValue, } = from(value, valuePath, context);
44
+ const { required: finalRequired, readonly: finalReadonly, value: finalValue, } = to(intermediateValue, valuePath, context);
45
45
  return {
46
46
  value: finalValue,
47
47
  required: intermediateRequired || finalRequired,
48
- readonly: intermediateDisabled || finalDisabled,
48
+ readonly: intermediateReadonly || finalReadonly,
49
49
  };
50
50
  };
51
51
  }
@@ -1,12 +1,12 @@
1
1
  import { type ReadonlyTypeOfType, type Type, type ValueOfType } from '@strictly/define';
2
2
  import { type AnnotatedFieldConversion, type TwoWayFieldConverterWithValueFactory, type UnreliableFieldConversion } from 'types/field_converters';
3
- export declare class NullableToBooleanConverter<T extends Type, E, ValuePath extends string, Context, NullType extends null | undefined> implements TwoWayFieldConverterWithValueFactory<ValueOfType<ReadonlyTypeOfType<T>> | NullType, boolean, E, ValuePath, Context> {
3
+ export declare class NullableToBooleanConverter<T extends Type, ValuePath extends string, Context, NullType extends null | undefined> implements TwoWayFieldConverterWithValueFactory<ValueOfType<ReadonlyTypeOfType<T>> | NullType, boolean, never, ValuePath, Context> {
4
4
  private readonly typeDef;
5
5
  private readonly prototype;
6
6
  private readonly nullType;
7
7
  readonly defaultValue: ValueOfType<ReadonlyTypeOfType<T>> | NullType;
8
8
  constructor(typeDef: T, prototype: ValueOfType<ReadonlyTypeOfType<T>>, nullType: NullType, defaultToNull?: boolean);
9
9
  convert(from: ValueOfType<ReadonlyTypeOfType<T>> | NullType): AnnotatedFieldConversion<boolean>;
10
- revert(from: boolean): UnreliableFieldConversion<ValueOfType<ReadonlyTypeOfType<T>> | NullType, E>;
10
+ revert(from: boolean): UnreliableFieldConversion<ValueOfType<ReadonlyTypeOfType<T>> | NullType, never>;
11
11
  create(): ValueOfType<ReadonlyTypeOfType<T>> | NullType;
12
12
  }
@@ -34,5 +34,6 @@ export function createCheckbox(valuePath, Checkbox) {
34
34
  onKeyUp,
35
35
  };
36
36
  };
37
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
37
38
  return createUnsafePartialObserverComponent(Checkbox, propSource, ['ErrorRenderer']);
38
39
  }
@@ -4,4 +4,4 @@ import type { AllFieldsOfFields } from 'types/all_fields_of_fields';
4
4
  import type { Fields } from 'types/field';
5
5
  import type { SubFormFields } from 'types/sub_form_fields';
6
6
  import type { MantineFieldComponent } from './types';
7
- export declare function createFieldsView<F extends Fields, K extends keyof AllFieldsOfFields<F>, P extends FieldsViewProps<Fields> = FieldsViewProps<SubFormFields<F, K>>>(valuePath: K, FieldsView: ComponentType<P>, observableProps: FieldsViewProps<F>): MantineFieldComponent<FieldsViewProps<P['fields']>, P>;
7
+ export declare function createFieldsView<F extends Fields, K extends keyof AllFieldsOfFields<F>, P extends FieldsViewProps<Fields> = FieldsViewProps<SubFormFields<F, K>>>(valuePath: K, FieldsView: ComponentType<P>, observableProps: FieldsViewProps<F>): MantineFieldComponent<FieldsViewProps<P['fields']>, P, never>;
@@ -31,9 +31,9 @@ export function createFieldsView(valuePath, FieldsView, observableProps) {
31
31
  acc[toSubKey(fieldKey)] = fieldValue;
32
32
  }
33
33
  return acc;
34
- }, {});
35
- return (_jsx(FieldsView, { ...props,
36
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
37
- fields: subFields, onFieldBlur: onFieldBlur, onFieldFocus: onFieldFocus, onFieldSubmit: onFieldSubmit, onFieldValueChange: onFieldValueChange }));
34
+ },
35
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
36
+ {});
37
+ return (_jsx(FieldsView, { ...props, fields: subFields, onFieldBlur: onFieldBlur, onFieldFocus: onFieldFocus, onFieldSubmit: onFieldSubmit, onFieldValueChange: onFieldValueChange }));
38
38
  });
39
39
  }
@@ -4,4 +4,4 @@ import { type AllFieldsOfFields } from 'types/all_fields_of_fields';
4
4
  import { type Fields } from 'types/field';
5
5
  import { type ValueTypeOfField } from 'types/value_type_of_field';
6
6
  import { type MantineFieldComponent } from './types';
7
- export declare function createForm<F extends Fields, K extends keyof AllFieldsOfFields<F>, P extends FormProps<ValueTypeOfField<F[K]>> = FormProps<ValueTypeOfField<F[K]>>>(valuePath: K, Form: ComponentType<P>, observableProps: FieldsViewProps<F>): MantineFieldComponent<FormProps<ValueTypeOfField<F[K]>>, P>;
7
+ export declare function createForm<F extends Fields, K extends keyof AllFieldsOfFields<F>, P extends FormProps<ValueTypeOfField<F[K]>> = FormProps<ValueTypeOfField<F[K]>>>(valuePath: K, Form: ComponentType<P>, observableProps: FieldsViewProps<F>): MantineFieldComponent<FormProps<ValueTypeOfField<F[K]>>, P, never>;
@@ -10,7 +10,7 @@ export type SuppliedListProps<Value = any, ListPath extends string = string> = {
10
10
  };
11
11
  export declare function createList<F extends Fields, K extends keyof ListFieldsOfFields<F>, Props extends SuppliedListProps<ElementOfArray<ValueTypeOfField<F[K]>>> & {
12
12
  children: (valuePath: `${K}.${number}`, value: ElementOfArray<ValueTypeOfField<F[K]>>, index: number) => React.ReactNode;
13
- }>(this: MantineForm<F>, valuePath: K, List: ComponentType<Props>): MantineFieldComponent<SuppliedListProps<ElementOfArray<ValueTypeOfField<F[K]>>>, Props>;
13
+ }>(this: MantineForm<F>, valuePath: K, List: ComponentType<Props>): MantineFieldComponent<SuppliedListProps<ElementOfArray<ValueTypeOfField<F[K]>>>, Props, never>;
14
14
  export declare function DefaultList<Value, ListPath extends string>({ values, listPath, children, }: SuppliedListProps<Value, ListPath> & {
15
15
  children: (valuePath: `${ListPath}.${number}`, value: Value, index: number) => React.ReactNode;
16
16
  }): import("react/jsx-runtime").JSX.Element;
@@ -4,4 +4,4 @@ import { type AllFieldsOfFields } from 'types/all_fields_of_fields';
4
4
  import { type Fields } from 'types/field';
5
5
  import { type MantineFieldComponent, type MantineForm } from './types';
6
6
  export type SuppliedPillProps = Pick<PillProps, 'children' | 'disabled'>;
7
- export declare function createPill<F extends Fields, K extends keyof AllFieldsOfFields<F>, Props extends SuppliedPillProps>(this: MantineForm<F>, valuePath: K, Pill: ComponentType<Props>): MantineFieldComponent<SuppliedPillProps, Props>;
7
+ export declare function createPill<F extends Fields, K extends keyof AllFieldsOfFields<F>, Props extends SuppliedPillProps>(this: MantineForm<F>, valuePath: K, Pill: ComponentType<Props>): MantineFieldComponent<SuppliedPillProps, Props, never>;
@@ -5,4 +5,4 @@ import { type StringFieldsOfFields } from 'types/string_fields_of_fields';
5
5
  import { type ValueTypeOfField } from 'types/value_type_of_field';
6
6
  import { type MantineFieldComponent, type MantineForm } from './types';
7
7
  export type SuppliedRadioProps = Pick<RadioProps, 'value' | 'disabled'>;
8
- export declare function createRadio<F extends Fields, K extends keyof StringFieldsOfFields<F>, Props extends SuppliedRadioProps>(this: MantineForm<F>, valuePath: K, value: ValueTypeOfField<F[K]>, Radio: ComponentType<Props>): MantineFieldComponent<SuppliedRadioProps, Props>;
8
+ export declare function createRadio<F extends Fields, K extends keyof StringFieldsOfFields<F>, Props extends SuppliedRadioProps>(this: MantineForm<F>, valuePath: K, value: ValueTypeOfField<F[K]>, Radio: ComponentType<Props>): MantineFieldComponent<SuppliedRadioProps, Props, never>;
@@ -31,5 +31,6 @@ export function createRadioGroup(valuePath, RadioGroup) {
31
31
  onKeyUp,
32
32
  };
33
33
  };
34
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
34
35
  return createUnsafePartialObserverComponent(RadioGroup, propSource, ['ErrorRenderer']);
35
36
  }
@@ -19,9 +19,13 @@ export function createTextInput(valuePath, TextInput) {
19
19
  }
20
20
  };
21
21
  const propSource = ({ ErrorRenderer = DefaultErrorRenderer, }) => {
22
- const { readonly, required, value, error,
23
22
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
24
- } = this.fields[valuePath];
23
+ const field = this.fields[valuePath];
24
+ if (field == null) {
25
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
26
+ throw new Error(`invalid field ${valuePath}`);
27
+ }
28
+ const { readonly, required, value, error, } = field;
25
29
  return {
26
30
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
27
31
  name: valuePath,
@@ -35,5 +39,6 @@ export function createTextInput(valuePath, TextInput) {
35
39
  onKeyUp,
36
40
  };
37
41
  };
42
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38
43
  return createUnsafePartialObserverComponent(TextInput, propSource, ['ErrorRenderer']);
39
44
  }
@@ -35,5 +35,6 @@ export function createValueInput(valuePath, ValueInput) {
35
35
  onKeyUp,
36
36
  };
37
37
  };
38
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
38
39
  return createUnsafePartialObserverComponent(ValueInput, propSource, ['ErrorRenderer']);
39
40
  }
@@ -1,6 +1,10 @@
1
1
  import { type ComponentType } from 'react';
2
- export type ErrorRendererProps<E> = {
2
+ import { type ErrorOfField } from 'types/error_of_field';
3
+ import { type Fields } from 'types/field';
4
+ type InternalErrorRendererProps<E> = {
3
5
  error: E;
4
6
  };
5
- export type ErrorRenderer<E = any> = ComponentType<ErrorRendererProps<E>>;
6
- export declare function DefaultErrorRenderer({ error, }: ErrorRendererProps<any>): any;
7
+ export type ErrorRendererProps<F extends Fields, K extends keyof Fields> = InternalErrorRendererProps<ErrorOfField<F[K]>>;
8
+ export type ErrorRenderer<E = any> = ComponentType<InternalErrorRendererProps<E>>;
9
+ export declare function DefaultErrorRenderer({ error, }: InternalErrorRendererProps<any>): string;
10
+ export {};
@@ -1,5 +1,5 @@
1
1
  export function DefaultErrorRenderer({ error,
2
2
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
3
3
  }) {
4
- return error;
4
+ return JSON.stringify(error);
5
5
  }
@@ -38,20 +38,20 @@ declare class MantineFormImpl<F extends Fields> implements MantineForm<F> {
38
38
  onFieldBlur: ((this: void, key: keyof F) => void) | undefined;
39
39
  onFieldSubmit: ((this: void, key: keyof F) => boolean | void) | undefined;
40
40
  constructor(fields: F);
41
- textInput<K extends keyof StringFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedTextInputProps, TextInputProps>;
42
- textInput<K extends keyof StringFieldsOfFields<F>, P extends SuppliedTextInputProps<any>>(valuePath: K, TextInput?: ComponentType<P>): MantineFieldComponent<SuppliedTextInputProps, P>;
41
+ textInput<K extends keyof StringFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedTextInputProps, TextInputProps, ErrorOfField<F[K]>>;
42
+ textInput<K extends keyof StringFieldsOfFields<F>, P extends SuppliedTextInputProps<any>>(valuePath: K, TextInput?: ComponentType<P>): MantineFieldComponent<SuppliedTextInputProps, P, ErrorOfField<F[K]>>;
43
43
  valueInput<K extends keyof AllFieldsOfFields<F>, P extends SuppliedValueInputProps<ValueTypeOfField<F[K]>, any>>(valuePath: K, ValueInput: ComponentType<P>): MantineFieldComponent<SuppliedValueInputProps<ValueTypeOfField<F[K]>>, P, ErrorOfField<F[K]>>;
44
44
  select<K extends keyof StringFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedTextInputProps, ComponentProps<typeof SimpleSelect>, ErrorOfField<F[K]>>;
45
- checkbox<K extends keyof BooleanFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedCheckboxProps, CheckboxProps>;
45
+ checkbox<K extends keyof BooleanFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedCheckboxProps, CheckboxProps, ErrorOfField<F[K]>>;
46
46
  checkbox<K extends keyof BooleanFieldsOfFields<F>, P extends SuppliedCheckboxProps>(valuePath: K, Checkbox: ComponentType<P>): MantineFieldComponent<SuppliedCheckboxProps, P, ErrorOfField<F[K]>>;
47
- radioGroup<K extends keyof StringFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedRadioGroupProps, RadioGroupProps>;
48
- radioGroup<K extends keyof StringFieldsOfFields<F>, P extends SuppliedRadioGroupProps>(valuePath: K, RadioGroup: ComponentType<P>): MantineFieldComponent<SuppliedRadioGroupProps, P>;
47
+ radioGroup<K extends keyof StringFieldsOfFields<F>, P extends RadioGroupProps = RadioGroupProps>(valuePath: K): MantineFieldComponent<SuppliedRadioGroupProps, P, ErrorOfField<F[K]>>;
48
+ radioGroup<K extends keyof StringFieldsOfFields<F>, P extends SuppliedRadioGroupProps>(valuePath: K, RadioGroup: ComponentType<P>): MantineFieldComponent<SuppliedRadioGroupProps, P, ErrorOfField<F[K]>>;
49
49
  radio<K extends keyof StringFieldsOfFields<F>>(valuePath: K, value: ValueTypeOfField<F[K]>): MantineFieldComponent<SuppliedRadioProps, RadioProps, ErrorOfField<F[K]>>;
50
50
  radio<K extends keyof StringFieldsOfFields<F>, P extends SuppliedRadioProps>(valuePath: K, value: ValueTypeOfField<F[K]>, Radio: ComponentType<P>): MantineFieldComponent<SuppliedRadioProps, P, ErrorOfField<F[K]>>;
51
51
  pill<K extends keyof AllFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedPillProps, PillProps, ErrorOfField<F[K]>>;
52
- pill<K extends keyof AllFieldsOfFields<F>, P extends SuppliedPillProps>(valuePath: K, Pill: ComponentType<P>): MantineFieldComponent<SuppliedPillProps, P>;
53
- list<K extends keyof ListFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedListProps<`${K}.${number}`>, ComponentProps<typeof DefaultList<ElementOfArray<F[K]['value']>, K>>>;
54
- fieldsView<K extends keyof AllFieldsOfFields<F>, P extends FieldsViewProps<Fields> = FieldsViewProps<SubFormFields<F, K>>>(valuePath: K, FieldsView: ComponentType<P>): MantineFieldComponent<FieldsViewProps<P['fields']>, P>;
55
- form<K extends keyof AllFieldsOfFields<F>, P extends FormProps<ValueTypeOfField<F[K]>> = FormProps<ValueTypeOfField<F[K]>>>(valuePath: K, Form: ComponentType<P>): MantineFieldComponent<FormProps<ValueTypeOfField<F[K]>>, P>;
52
+ pill<K extends keyof AllFieldsOfFields<F>, P extends SuppliedPillProps>(valuePath: K, Pill: ComponentType<P>): MantineFieldComponent<SuppliedPillProps, P, ErrorOfField<F[K]>>;
53
+ list<K extends keyof ListFieldsOfFields<F>>(valuePath: K): MantineFieldComponent<SuppliedListProps<`${K}.${number}`>, ComponentProps<typeof DefaultList<ElementOfArray<F[K]['value']>, K>>, never>;
54
+ fieldsView<K extends keyof AllFieldsOfFields<F>, P extends FieldsViewProps<Fields> = FieldsViewProps<SubFormFields<F, K>>>(valuePath: K, FieldsView: ComponentType<P>): MantineFieldComponent<FieldsViewProps<P['fields']>, P, never>;
55
+ form<K extends keyof AllFieldsOfFields<F>, P extends FormProps<ValueTypeOfField<F[K]>> = FormProps<ValueTypeOfField<F[K]>>>(valuePath: K, Form: ComponentType<P>): MantineFieldComponent<FormProps<ValueTypeOfField<F[K]>>, P, never>;
56
56
  }
57
57
  export {};
@@ -1,12 +1,9 @@
1
1
  import { type Meta, type StoryObj } from '@storybook/react';
2
2
  import { type FieldsViewProps } from 'core/props';
3
- import { type ErrorRenderer } from 'mantine/error_renderer';
4
3
  import { type Field } from 'types/field';
5
- declare function Component({ ErrorRenderer, ...props }: FieldsViewProps<{
4
+ declare function Component({ ...props }: FieldsViewProps<{
6
5
  $: Field<boolean, string>;
7
- }> & {
8
- ErrorRenderer?: ErrorRenderer;
9
- }): import("react/jsx-runtime").JSX.Element;
6
+ }>): import("react/jsx-runtime").JSX.Element;
10
7
  declare const meta: Meta<typeof Component>;
11
8
  export default meta;
12
9
  type Story = StoryObj<typeof Component>;
@@ -15,4 +12,3 @@ export declare const On: Story;
15
12
  export declare const Required: Story;
16
13
  export declare const Disabled: Story;
17
14
  export declare const Error: Story;
18
- export declare const CustomError: Story;
@@ -2,7 +2,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { action } from '@storybook/addon-actions';
3
3
  import { useMantineFormFields } from 'mantine/hooks';
4
4
  import { CHECKBOX_LABEL } from './checkbox_constants';
5
- function Component({ ErrorRenderer, ...props }) {
5
+ function ErrorRenderer({ error }) {
6
+ return `Error ${error}`;
7
+ }
8
+ function Component({ ...props }) {
6
9
  const inputProps = useMantineFormFields(props);
7
10
  const CheckboxComponent = inputProps.checkbox('$');
8
11
  return (_jsx(CheckboxComponent, { ErrorRenderer: ErrorRenderer, label: CHECKBOX_LABEL }));
@@ -73,18 +76,3 @@ export const Error = {
73
76
  },
74
77
  },
75
78
  };
76
- export const CustomError = {
77
- args: {
78
- fields: {
79
- $: {
80
- readonly: false,
81
- required: false,
82
- value: true,
83
- error: 'error',
84
- },
85
- },
86
- ErrorRenderer: function () {
87
- return 'custom error';
88
- },
89
- },
90
- };
@@ -12,4 +12,4 @@ export declare const Empty: Story;
12
12
  export declare const Populated: Story;
13
13
  export declare const Required: Story;
14
14
  export declare const Disabled: Story;
15
- export declare const CustomError: Story;
15
+ export declare const Errors: Story;
@@ -3,16 +3,19 @@ import { Button, Stack, } from '@mantine/core';
3
3
  import { action } from '@storybook/addon-actions';
4
4
  import { useMantineFormFields } from 'mantine/hooks';
5
5
  const onClick = action('some button clicked');
6
+ function ErrorRenderer({ error }) {
7
+ return `error ${error}`;
8
+ }
6
9
  function SubFieldsView(props) {
7
10
  const form = useMantineFormFields(props);
8
11
  const TextInput = form.textInput('$');
9
- return (_jsxs(Stack, { children: [_jsx(TextInput, { label: 'sub fields view' }), _jsx(Button, { onClick: props.onClick, children: "Bonus Button" })] }));
12
+ return (_jsxs(Stack, { children: [_jsx(TextInput, { ErrorRenderer: ErrorRenderer, label: 'sub fields view' }), _jsx(Button, { onClick: props.onClick, children: "Bonus Button" })] }));
10
13
  }
11
14
  function Component(props) {
12
15
  const form = useMantineFormFields(props);
13
16
  const FieldsView = form.fieldsView('$.a', SubFieldsView);
14
17
  const TextInput = form.textInput('$');
15
- return (_jsxs(Stack, { children: [_jsx(TextInput, { label: 'fields view' }), _jsx(FieldsView, { onClick: onClick })] }));
18
+ return (_jsxs(Stack, { children: [_jsx(TextInput, { ErrorRenderer: ErrorRenderer, label: 'fields view' }), _jsx(FieldsView, { onClick: onClick })] }));
16
19
  }
17
20
  const meta = {
18
21
  component: Component,
@@ -88,7 +91,7 @@ export const Disabled = {
88
91
  },
89
92
  },
90
93
  };
91
- export const CustomError = {
94
+ export const Errors = {
92
95
  args: {
93
96
  fields: {
94
97
  $: {
@@ -2,8 +2,8 @@ import { type Meta, type StoryObj } from '@storybook/react';
2
2
  import { type FieldsViewProps } from 'core/props';
3
3
  import { type Field } from 'types/field';
4
4
  declare function Component(props: FieldsViewProps<{
5
- $: Field<string>;
6
- '$.a': Field<number>;
5
+ $: Field<string, string>;
6
+ '$.a': Field<number, string>;
7
7
  }>): import("react/jsx-runtime").JSX.Element;
8
8
  declare const meta: Meta<typeof Component>;
9
9
  export default meta;
@@ -4,6 +4,9 @@ import { action } from '@storybook/addon-actions';
4
4
  import { useMantineFormFields } from 'mantine/hooks';
5
5
  import { useCallback } from 'react';
6
6
  const onCancel = action('canceled');
7
+ function ErrorRenderer({ error }) {
8
+ return `error ${error}`;
9
+ }
7
10
  function SubForm({ value, onValueChange, onCancel, }) {
8
11
  const onChange = useCallback((v) => {
9
12
  onValueChange(Number.parseInt(`${v}`));
@@ -14,7 +17,7 @@ function Component(props) {
14
17
  const form = useMantineFormFields(props);
15
18
  const Form = form.form('$.a', SubForm);
16
19
  const TextInput = form.textInput('$');
17
- return (_jsxs(Stack, { children: [_jsx(TextInput, { label: 'fields view' }), _jsx(Form, { onCancel: onCancel })] }));
20
+ return (_jsxs(Stack, { children: [_jsx(TextInput, { ErrorRenderer: ErrorRenderer, label: 'fields view' }), _jsx(Form, { onCancel: onCancel })] }));
18
21
  }
19
22
  const meta = {
20
23
  component: Component,