@strictly/react-form 0.0.9 → 0.0.11

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 (60) hide show
  1. package/.out/core/mobx/field_adapter_builder.js +18 -6
  2. package/.out/core/mobx/form_presenter.d.ts +5 -5
  3. package/.out/core/mobx/form_presenter.js +232 -127
  4. package/.out/core/mobx/hooks.d.ts +24 -4
  5. package/.out/core/mobx/hooks.js +26 -5
  6. package/.out/core/mobx/merge_field_adapters_with_validators.js +1 -5
  7. package/.out/core/mobx/specs/fixtures.js +2 -1
  8. package/.out/core/mobx/specs/form_presenter.tests.js +16 -8
  9. package/.out/core/mobx/specs/sub_form_field_adapters.tests.js +2 -1
  10. package/.out/core/mobx/sub_form_field_adapters.d.ts +2 -2
  11. package/.out/field_converters/integer_to_string_converter.js +12 -4
  12. package/.out/field_converters/maybe_identity_converter.js +12 -4
  13. package/.out/field_converters/nullable_to_boolean_converter.js +24 -7
  14. package/.out/field_converters/select_value_type_converter.js +36 -12
  15. package/.out/mantine/create_checkbox.js +8 -4
  16. package/.out/mantine/create_fields_view.d.ts +9 -1
  17. package/.out/mantine/create_fields_view.js +20 -5
  18. package/.out/mantine/create_form.js +1 -1
  19. package/.out/mantine/create_radio_group.js +8 -4
  20. package/.out/mantine/create_text_input.js +8 -4
  21. package/.out/mantine/create_value_input.js +8 -4
  22. package/.out/mantine/hooks.d.ts +2 -1
  23. package/.out/mantine/hooks.js +219 -93
  24. package/.out/mantine/specs/checkbox_hooks.stories.js +13 -1
  25. package/.out/mantine/specs/checkbox_hooks.tests.js +22 -9
  26. package/.out/mantine/specs/create_fields_view.tests.d.ts +1 -0
  27. package/.out/mantine/specs/create_fields_view.tests.js +17 -0
  28. package/.out/mantine/specs/fields_view_hooks.stories.d.ts +6 -2
  29. package/.out/mantine/specs/fields_view_hooks.stories.js +39 -7
  30. package/.out/mantine/specs/fields_view_hooks.tests.js +30 -1
  31. package/.out/mantine/specs/radio_group_hooks.stories.js +13 -1
  32. package/.out/mantine/specs/radio_group_hooks.tests.js +23 -10
  33. package/.out/mantine/specs/select_hooks.stories.js +13 -1
  34. package/.out/mantine/specs/text_input_hooks.stories.js +13 -1
  35. package/.out/mantine/specs/text_input_hooks.tests.js +18 -7
  36. package/.out/mantine/specs/value_input_hooks.stories.js +14 -2
  37. package/.out/tsconfig.tsbuildinfo +1 -1
  38. package/.out/tsup.config.js +2 -9
  39. package/.out/types/merge_validators.js +1 -4
  40. package/.out/util/partial.js +5 -5
  41. package/.out/vitest.workspace.js +2 -10
  42. package/.turbo/turbo-build.log +9 -9
  43. package/.turbo/turbo-check-types.log +1 -1
  44. package/.turbo/turbo-release$colon$exports.log +1 -1
  45. package/core/mobx/form_presenter.ts +15 -14
  46. package/core/mobx/hooks.tsx +197 -0
  47. package/core/mobx/specs/form_presenter.tests.ts +24 -5
  48. package/core/mobx/sub_form_field_adapters.ts +14 -3
  49. package/dist/index.cjs +395 -277
  50. package/dist/index.d.cts +52 -26
  51. package/dist/index.d.ts +52 -26
  52. package/dist/index.js +398 -276
  53. package/mantine/create_fields_view.tsx +66 -31
  54. package/mantine/hooks.tsx +9 -6
  55. package/mantine/specs/__snapshots__/fields_view_hooks.tests.tsx.snap +194 -197
  56. package/mantine/specs/create_fields_view.tests.ts +29 -0
  57. package/mantine/specs/fields_view_hooks.stories.tsx +58 -15
  58. package/mantine/specs/fields_view_hooks.tests.tsx +26 -0
  59. package/package.json +1 -1
  60. package/core/mobx/hooks.ts +0 -112
@@ -1,12 +1,5 @@
1
1
  import { defineConfig, } from 'tsup';
2
- export default defineConfig((options) => ({
3
- entry: ['index.ts'],
4
- tsconfig: './tsconfig.build.json',
5
- clean: false,
6
- dts: true,
7
- format: [
2
+ export default defineConfig((options) => (Object.assign({ entry: ['index.ts'], tsconfig: './tsconfig.build.json', clean: false, dts: true, format: [
8
3
  'cjs',
9
4
  'esm',
10
- ],
11
- ...options,
12
- }));
5
+ ] }, options)));
@@ -1,9 +1,6 @@
1
1
  import { annotations, validate, } from '@strictly/define';
2
2
  export function mergeValidators(validators1, validators2) {
3
- const validators = {
4
- ...validators1,
5
- ...validators2,
6
- };
3
+ const validators = Object.assign(Object.assign({}, validators1), validators2);
7
4
  const keys1 = new Set(Object.keys(validators1));
8
5
  const keys2 = new Set(Object.keys(validators2));
9
6
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -8,7 +8,7 @@ export function createSimplePartialComponent(Component, curriedProps) {
8
8
  // still needs a cast as `extends ComponentType<any>` != `ComponentType<any>`
9
9
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unnecessary-type-assertion
10
10
  const C = Component;
11
- return (_jsx(C, { ref: ref, ...curriedProps, ...exposedProps }));
11
+ return (_jsx(C, Object.assign({ ref: ref }, curriedProps, exposedProps)));
12
12
  });
13
13
  }
14
14
  export function createPartialComponent(Component, curriedPropsSource, additionalPropKeys = []) {
@@ -34,11 +34,11 @@ export function createPartialComponent(Component, curriedPropsSource, additional
34
34
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
35
35
  {},
36
36
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
37
- { ...props },
37
+ Object.assign({}, props),
38
38
  ]);
39
39
  // TODO is there any way we can memoize this transformation?
40
40
  const curriedProps = curriedPropsSource(additionalProps);
41
- return (_jsx(C, { ref: ref, ...curriedProps, ...exposedProps }));
41
+ return (_jsx(C, Object.assign({ ref: ref }, curriedProps, exposedProps)));
42
42
  });
43
43
  }
44
44
  export function usePartialComponent(
@@ -88,11 +88,11 @@ export function createUnsafePartialObserverComponent(Component, curriedPropsSour
88
88
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
89
89
  {},
90
90
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
91
- { ...props },
91
+ Object.assign({}, props),
92
92
  ]);
93
93
  // TODO is there any way we can memoize this transformation?
94
94
  const curriedProps = curriedPropsSource(additionalProps);
95
- return (_jsx(C, { ref: ref, ...curriedProps, ...exposedProps }));
95
+ return (_jsx(C, Object.assign({ ref: ref }, curriedProps, exposedProps)));
96
96
  }));
97
97
  }
98
98
  export function usePartialObserverComponent(
@@ -4,19 +4,11 @@ import tsconfig from './tsconfig.json';
4
4
  const config = createVitestUserConfig(tsconfig);
5
5
  export default defineWorkspace([
6
6
  '.',
7
- {
8
- ...config,
9
- extends: './.storybook/vite.config.mts',
10
- test: {
11
- ...(config.test || {}),
12
- environment: 'jsdom',
13
- setupFiles: [
7
+ Object.assign(Object.assign({}, config), { extends: './.storybook/vite.config.mts', test: Object.assign(Object.assign({}, (config.test || {})), { environment: 'jsdom', setupFiles: [
14
8
  './.vitest/install_deterministic_random.ts',
15
9
  // install storybook setup for unit tests that import stories directly
16
10
  './.vitest/install_storybook_preview.ts',
17
11
  './.vitest/match_media.ts',
18
12
  './.vitest/resize_observer.ts',
19
- ],
20
- },
21
- },
13
+ ] }) }),
22
14
  ]);
@@ -4,15 +4,15 @@ $ tsup
4
4
  CLI Using tsconfig: tsconfig.build.json
5
5
  CLI tsup v8.3.5
6
6
  CLI Using tsup config: /home/runner/work/strictly/strictly/packages/react-form/tsup.config.ts
7
- CLI Target: esnext
7
+ CLI Target: es6
8
8
  CJS Build start
9
9
  ESM Build start
10
- CJS dist/index.cjs 51.55 KB
11
- CJS ⚡️ Build success in 120ms
12
- ESM dist/index.js 47.61 KB
13
- ESM ⚡️ Build success in 121ms
10
+ ESM dist/index.js 56.04 KB
11
+ ESM ⚡️ Build success in 111ms
12
+ CJS dist/index.cjs 59.85 KB
13
+ CJS ⚡️ Build success in 113ms
14
14
  DTS Build start
15
- DTS ⚡️ Build success in 10031ms
16
- DTS dist/index.d.cts 37.40 KB
17
- DTS dist/index.d.ts 37.40 KB
18
- Done in 11.07s.
15
+ DTS ⚡️ Build success in 9897ms
16
+ DTS dist/index.d.cts 39.16 KB
17
+ DTS dist/index.d.ts 39.16 KB
18
+ Done in 10.95s.
@@ -1,3 +1,3 @@
1
1
  yarn run v1.22.22
2
2
  $ tsc
3
- Done in 7.61s.
3
+ Done in 7.46s.
@@ -1,3 +1,3 @@
1
1
  yarn run v1.22.22
2
2
  $ json -f package.json -f package.exports.json --merge > package.release.json
3
- Done in 0.12s.
3
+ Done in 0.11s.
@@ -102,7 +102,7 @@ export type ValuePathsToAdaptersOf<
102
102
  }
103
103
  : never
104
104
 
105
- export class FormPresenter<
105
+ export abstract class FormPresenter<
106
106
  T extends Type,
107
107
  ValueToTypePaths extends Readonly<Record<string, string>>,
108
108
  TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<
@@ -116,7 +116,7 @@ export class FormPresenter<
116
116
  > {
117
117
  constructor(
118
118
  readonly type: T,
119
- private readonly adapters: TypePathsToAdapters,
119
+ protected readonly adapters: TypePathsToAdapters,
120
120
  ) {
121
121
  }
122
122
 
@@ -361,7 +361,7 @@ export class FormPresenter<
361
361
  }
362
362
  }
363
363
 
364
- clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(
364
+ clearFieldValue<K extends StringKeyOf<ValueToTypePaths>>(
365
365
  model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>,
366
366
  valuePath: K,
367
367
  ) {
@@ -375,8 +375,7 @@ export class FormPresenter<
375
375
  convert,
376
376
  create,
377
377
  } = adapter
378
- const accessor = model.accessors[valuePath]
379
- const value = accessor == null ? create(valuePath, model.value) : accessor.value
378
+ const value = create(valuePath, model.value)
380
379
  const {
381
380
  value: displayValue,
382
381
  } = convert(value, valuePath, model.value)
@@ -412,6 +411,7 @@ export class FormPresenter<
412
411
  validateField<K extends keyof ValuePathsToAdapters>(
413
412
  model: FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>,
414
413
  valuePath: K,
414
+ ignoreDefaultValue = false,
415
415
  ): boolean {
416
416
  const {
417
417
  convert,
@@ -436,7 +436,14 @@ export class FormPresenter<
436
436
  : storedValue
437
437
  const dirty = storedValue !== value
438
438
  assertExists(revert, 'changing field directly not supported {}', valuePath)
439
-
439
+ if (ignoreDefaultValue) {
440
+ const {
441
+ value: defaultDisplayValue,
442
+ } = convert(create(valuePath, model.value), valuePath, model.value)
443
+ if (defaultDisplayValue === value) {
444
+ return true
445
+ }
446
+ }
440
447
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
441
448
  const conversion = revert(value, valuePath as string, model.value)
442
449
  return runInAction(function () {
@@ -521,18 +528,12 @@ export class FormPresenter<
521
528
  })
522
529
  }
523
530
 
524
- createModel(value: ValueOfType<ReadonlyTypeOfType<T>>): FormModel<
531
+ abstract createModel(value: ValueOfType<ReadonlyTypeOfType<T>>): FormModel<
525
532
  T,
526
533
  ValueToTypePaths,
527
534
  TypePathsToAdapters,
528
535
  ValuePathsToAdapters
529
- > {
530
- return new FormModel<T, ValueToTypePaths, TypePathsToAdapters, ValuePathsToAdapters>(
531
- this.type,
532
- value,
533
- this.adapters,
534
- )
535
- }
536
+ >
536
537
  }
537
538
 
538
539
  export class FormModel<
@@ -0,0 +1,197 @@
1
+ import {
2
+ type ReadonlyTypeOfType,
3
+ type ValueOfType,
4
+ } from '@strictly/define'
5
+ import {
6
+ type FieldsViewProps,
7
+ } from 'core/props'
8
+ import {
9
+ type ComponentType,
10
+ useCallback,
11
+ useMemo,
12
+ } from 'react'
13
+ import {
14
+ createUnsafePartialObserverComponent,
15
+ type UnsafePartialComponent,
16
+ } from 'util/partial'
17
+ import { type FormPresenter } from './form_presenter'
18
+ import {
19
+ type FormFieldsOfPresenter,
20
+ type ToValueOfPresenterValuePath,
21
+ type ValuePathsOfPresenter,
22
+ } from './types'
23
+
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ type ValueOfPresenter<P extends FormPresenter<any, any, any, any>> = P extends FormPresenter<infer T, any, any, any>
26
+ ? ValueOfType<ReadonlyTypeOfType<T>>
27
+ : never
28
+
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ type ModelOfPresenter<P extends FormPresenter<any, any, any, any>> = ReturnType<P['createModel']>
31
+
32
+ export function useDefaultMobxFormHooks<
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ P extends FormPresenter<any, any, any, any>,
35
+ C extends ComponentType<FieldsViewProps<F>>,
36
+ F extends FormFieldsOfPresenter<P> = FormFieldsOfPresenter<P>,
37
+ >(
38
+ presenter: P,
39
+ value: ValueOfPresenter<P>,
40
+ options?: {
41
+ onValidFieldSubmit?: <Path extends ValuePathsOfPresenter<P>>(model: ModelOfPresenter<P>, valuePath: Path) => void,
42
+ onValidFormSubmit?: (model: ModelOfPresenter<P>, value: ValueOfPresenter<P>) => void,
43
+ },
44
+ ): {
45
+ model: ModelOfPresenter<P>,
46
+ FormFields?: UnsafePartialComponent<C, FieldsViewProps<F>>,
47
+ onFormSubmit: () => void,
48
+ onFieldValueChange<K extends keyof F>(this: void, key: K, value: F[K]['value']): void,
49
+ onFieldFocus?(this: void, key: keyof F): void,
50
+ onFieldBlur?(this: void, key: keyof F): void,
51
+ onFieldSubmit?(this: void, key: keyof F): boolean | void,
52
+ }
53
+ export function useDefaultMobxFormHooks<
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ P extends FormPresenter<any, any, any, any>,
56
+ C extends ComponentType<FieldsViewProps<F>>,
57
+ F extends FormFieldsOfPresenter<P> = FormFieldsOfPresenter<P>,
58
+ >(
59
+ presenter: P,
60
+ value: ValueOfPresenter<P>,
61
+ options: {
62
+ onValidFieldSubmit?: <Path extends ValuePathsOfPresenter<P>>(model: ModelOfPresenter<P>, valuePath: Path) => void,
63
+ onValidFormSubmit?: (model: ModelOfPresenter<P>, value: ValueOfPresenter<P>) => void,
64
+ // TODO the types of C are not working, needs a local implementation to test
65
+ FormFieldsView: C,
66
+ },
67
+ ): {
68
+ model: ModelOfPresenter<P>,
69
+ FormFields: UnsafePartialComponent<C, FieldsViewProps<F>>,
70
+ onFormSubmit: () => void,
71
+ onFieldValueChange<K extends keyof F>(this: void, key: K, value: F[K]['value']): void,
72
+ onFieldFocus?(this: void, key: keyof F): void,
73
+ onFieldBlur?(this: void, key: keyof F): void,
74
+ onFieldSubmit?(this: void, key: keyof F): boolean | void,
75
+ }
76
+ export function useDefaultMobxFormHooks<
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ P extends FormPresenter<any, any, any, any>,
79
+ C extends FieldsViewProps<F>,
80
+ F extends FormFieldsOfPresenter<P> = FormFieldsOfPresenter<P>,
81
+ >(
82
+ presenter: P,
83
+ value: ValueOfPresenter<P>,
84
+ {
85
+ onValidFieldSubmit,
86
+ onValidFormSubmit,
87
+ FormFieldsView,
88
+ }: {
89
+ onValidFieldSubmit?: <Path extends ValuePathsOfPresenter<P>>(model: ModelOfPresenter<P>, valuePath: Path) => void,
90
+ onValidFormSubmit?: (model: ModelOfPresenter<P>, value: ValueOfPresenter<P>) => void,
91
+ FormFieldsView?: ComponentType<C>,
92
+ } = {},
93
+ ): {
94
+ model: ModelOfPresenter<P>,
95
+ FormFields?: UnsafePartialComponent<ComponentType<C>, FieldsViewProps<F>> | undefined,
96
+ onFormSubmit: () => void,
97
+ onFieldValueChange<K extends keyof F>(this: void, key: K, value: F[K]['value']): void,
98
+ onFieldFocus?(this: void, key: keyof F): void,
99
+ onFieldBlur?(this: void, key: keyof F): void,
100
+ onFieldSubmit?(this: void, key: keyof F): boolean | void,
101
+ } {
102
+ const model = useMemo(function () {
103
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
104
+ return presenter.createModel(value) as ReturnType<P['createModel']>
105
+ }, [
106
+ presenter,
107
+ value,
108
+ ])
109
+
110
+ const onFieldValueChange = useCallback(
111
+ function<Path extends ValuePathsOfPresenter<P>> (
112
+ path: Path,
113
+ value: ToValueOfPresenterValuePath<P, Path>,
114
+ ) {
115
+ presenter.clearFieldError(model, path)
116
+ presenter.setFieldValue<Path>(model, path, value)
117
+ },
118
+ [
119
+ presenter,
120
+ model,
121
+ ],
122
+ )
123
+
124
+ const onFieldSubmit = useCallback(
125
+ function<Path extends ValuePathsOfPresenter<P>> (valuePath: Path) {
126
+ if (presenter.validateField(model, valuePath)) {
127
+ onValidFieldSubmit?.(model, valuePath)
128
+ }
129
+ return false
130
+ },
131
+ [
132
+ presenter,
133
+ model,
134
+ onValidFieldSubmit,
135
+ ],
136
+ )
137
+
138
+ const onFieldBlur = useCallback(
139
+ function<Path extends ValuePathsOfPresenter<P>> (path: Path) {
140
+ // work around potential loss of focus prior to state potentially invalidating change triggering
141
+ // (e.g. changing a discriminator)
142
+ // TODO debounce?
143
+ setTimeout(function () {
144
+ if (presenter.isValuePathActive(model, path)) {
145
+ presenter.validateField(model, path, true)
146
+ }
147
+ }, 100)
148
+ },
149
+ [
150
+ presenter,
151
+ model,
152
+ ],
153
+ )
154
+
155
+ const onFormSubmit = useCallback(
156
+ function () {
157
+ if (presenter.validateAll(model)) {
158
+ onValidFormSubmit?.(model, model.value)
159
+ }
160
+ },
161
+ [
162
+ presenter,
163
+ model,
164
+ onValidFormSubmit,
165
+ ],
166
+ )
167
+
168
+ const FormFields = useMemo(() => {
169
+ if (FormFieldsView == null) {
170
+ return undefined
171
+ }
172
+ return createUnsafePartialObserverComponent(FormFieldsView, (): FieldsViewProps<F> => {
173
+ return {
174
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
175
+ fields: model.fields as C['fields'],
176
+ onFieldBlur,
177
+ onFieldSubmit,
178
+ onFieldValueChange,
179
+ }
180
+ })
181
+ }, [
182
+ model,
183
+ FormFieldsView,
184
+ onFieldBlur,
185
+ onFieldSubmit,
186
+ onFieldValueChange,
187
+ ])
188
+
189
+ return {
190
+ model,
191
+ onFieldValueChange,
192
+ onFieldSubmit,
193
+ onFieldBlur,
194
+ onFormSubmit,
195
+ FormFields,
196
+ }
197
+ }
@@ -6,10 +6,14 @@ import {
6
6
  nullType,
7
7
  numberType,
8
8
  object,
9
+ type ReadonlyOfTypeDef,
10
+ type ReadonlyTypeOfType,
9
11
  record,
10
12
  stringType,
13
+ type Type,
11
14
  union,
12
15
  type ValueOfType,
16
+ type ValueOfTypeDef,
13
17
  type ValueToTypePathsOfType,
14
18
  } from '@strictly/define'
15
19
  import { type FieldAdapter } from 'core/mobx/field_adapter'
@@ -39,6 +43,21 @@ import {
39
43
 
40
44
  const IS_NAN_ERROR = 1
41
45
 
46
+ class TestFormPresenter<
47
+ T extends Type,
48
+ ValueToTypePaths extends Readonly<Record<string, string>>,
49
+ TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<
50
+ FlattenedValuesOfType<T, '*'>,
51
+ ValueOfType<ReadonlyTypeOfType<T>>
52
+ >,
53
+ > extends FormPresenter<T, ValueToTypePaths, TypePathsToAdapters> {
54
+ override createModel(value: ValueOfTypeDef<ReadonlyOfTypeDef<T['definition']>, {}>): FormModel<T, ValueToTypePaths,
55
+ TypePathsToAdapters, ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths>>
56
+ {
57
+ return new FormModel(this.type, value, this.adapters)
58
+ }
59
+ }
60
+
42
61
  const originalIntegerToStringAdapter = adapterFromTwoWayConverter(
43
62
  new IntegerToStringConverter(IS_NAN_ERROR),
44
63
  prototypingFieldValueFactory(0),
@@ -479,7 +498,7 @@ describe('all', function () {
479
498
  const adapters = {
480
499
  $: integerToStringAdapter,
481
500
  } as const
482
- const presenter = new FormPresenter<
501
+ const presenter = new TestFormPresenter<
483
502
  typeof typeDef,
484
503
  ValueToTypePathsOfType<typeof typeDef>,
485
504
  typeof adapters
@@ -600,7 +619,7 @@ describe('all', function () {
600
619
  const converters = {
601
620
  '$.*': integerToStringAdapter,
602
621
  } as const
603
- const presenter = new FormPresenter<
622
+ const presenter = new TestFormPresenter<
604
623
  typeof typeDef,
605
624
  ValueToTypePathsOfType<typeof typeDef>,
606
625
  typeof converters
@@ -927,7 +946,7 @@ describe('all', function () {
927
946
  '$.*': integerToStringAdapter,
928
947
  } as const
929
948
  type ValueToTypePaths = ValueToTypePathsOfType<typeof type>
930
- const presenter = new FormPresenter<
949
+ const presenter = new TestFormPresenter<
931
950
  typeof type,
932
951
  ValueToTypePaths,
933
952
  typeof adapters
@@ -997,7 +1016,7 @@ describe('all', function () {
997
1016
  '$.x:a': identityAdapter(0).narrow,
998
1017
  '$.y:b': identityAdapter(false).narrow,
999
1018
  } as const
1000
- const presenter = new FormPresenter<
1019
+ const presenter = new TestFormPresenter<
1001
1020
  typeof type,
1002
1021
  ValueToTypePaths,
1003
1022
  typeof adapters
@@ -1068,7 +1087,7 @@ describe('all', function () {
1068
1087
  $: '$',
1069
1088
  '$.fake': '$.fake',
1070
1089
  }
1071
- const presenter = new FormPresenter<
1090
+ const presenter = new TestFormPresenter<
1072
1091
  typeof typeDef,
1073
1092
  JsonPaths,
1074
1093
  typeof converters
@@ -1,6 +1,7 @@
1
1
  import { type StringConcatOf } from '@strictly/base'
2
2
  import {
3
3
  flattenValuesOfType,
4
+ type ReadonlyTypeOfType,
4
5
  type Type,
5
6
  type ValueOfType,
6
7
  } from '@strictly/define'
@@ -47,10 +48,15 @@ export function subFormFieldAdapters<
47
48
  subAdapters: SubAdapters,
48
49
  parentTypePath: TypePath,
49
50
  contextType: ContextType,
50
- ): SubFormFieldAdapters<SubAdapters, TypePath, TypePathsToValuePaths[TypePath], ValueOfType<ContextType>> {
51
+ ): SubFormFieldAdapters<
52
+ SubAdapters,
53
+ TypePath,
54
+ TypePathsToValuePaths[TypePath],
55
+ ValueOfType<ReadonlyTypeOfType<ContextType>>
56
+ > {
51
57
  // assume the number of '.' in the type path will correspond to the number of '.' in the value path
52
58
  const dotCount = parentTypePath.split('.').length
53
- function getSubValuePathAndContext(valuePath: string, context: ValueOfType<ContextType>) {
59
+ function getSubValuePathAndContext(valuePath: string, context: ValueOfType<ReadonlyTypeOfType<ContextType>>) {
54
60
  const parentValuePath = valuePath.split('.').slice(0, dotCount).join('.')
55
61
  const subValuePath = valuePath.replace(parentValuePath, '$')
56
62
  const subContext = flattenValuesOfType(contextType, context)[parentValuePath]
@@ -80,5 +86,10 @@ export function subFormFieldAdapters<
80
86
  }
81
87
  acc[typePath] = adaptedAdapter
82
88
  return acc
83
- }, {}) as SubFormFieldAdapters<SubAdapters, TypePath, TypePathsToValuePaths[TypePath], ValueOfType<ContextType>>
89
+ }, {}) as SubFormFieldAdapters<
90
+ SubAdapters,
91
+ TypePath,
92
+ TypePathsToValuePaths[TypePath],
93
+ ValueOfType<ReadonlyTypeOfType<ContextType>>
94
+ >
84
95
  }