@strictly/react-form 0.0.14 → 0.0.16

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 (42) hide show
  1. package/.out/core/mobx/field_adapter_builder.d.ts +1 -1
  2. package/.out/core/mobx/form_model.d.ts +7 -5
  3. package/.out/core/mobx/form_model.js +27 -23
  4. package/.out/core/mobx/merge_field_adapters_with_validators.d.ts +1 -1
  5. package/.out/core/mobx/specs/form_model.tests.js +18 -8
  6. package/.out/core/mobx/types.d.ts +4 -4
  7. package/.out/index.d.ts +0 -1
  8. package/.out/index.js +0 -1
  9. package/.out/mantine/create_field_view.d.ts +20 -0
  10. package/.out/mantine/create_field_view.js +54 -0
  11. package/.out/mantine/create_list.js +3 -2
  12. package/.out/mantine/hooks.d.ts +4 -1
  13. package/.out/mantine/hooks.js +13 -1
  14. package/.out/mantine/specs/field_view_hooks.stories.d.ts +12 -0
  15. package/.out/mantine/specs/field_view_hooks.stories.js +61 -0
  16. package/.out/mantine/specs/field_view_hooks.tests.d.ts +1 -0
  17. package/.out/mantine/specs/field_view_hooks.tests.js +12 -0
  18. package/.out/tsconfig.tsbuildinfo +1 -1
  19. package/.out/types/merge_validators.d.ts +1 -1
  20. package/.turbo/turbo-build.log +8 -8
  21. package/.turbo/turbo-check-types.log +1 -1
  22. package/core/mobx/field_adapter_builder.ts +2 -2
  23. package/core/mobx/form_model.ts +48 -23
  24. package/core/mobx/merge_field_adapters_with_validators.ts +1 -1
  25. package/core/mobx/specs/form_model.tests.ts +28 -11
  26. package/core/mobx/types.ts +4 -4
  27. package/dist/index.cjs +124 -70
  28. package/dist/index.d.cts +31 -28
  29. package/dist/index.d.ts +31 -28
  30. package/dist/index.js +121 -62
  31. package/index.ts +0 -1
  32. package/mantine/create_field_view.tsx +94 -0
  33. package/mantine/create_list.tsx +9 -2
  34. package/mantine/hooks.tsx +18 -1
  35. package/mantine/specs/__snapshots__/field_view_hooks.tests.tsx.snap +153 -0
  36. package/mantine/specs/field_view_hooks.stories.tsx +112 -0
  37. package/mantine/specs/field_view_hooks.tests.tsx +15 -0
  38. package/package.json +1 -1
  39. package/types/merge_validators.ts +2 -2
  40. package/.out/mantine/field_view.d.ts +0 -18
  41. package/.out/mantine/field_view.js +0 -16
  42. package/mantine/field_view.tsx +0 -39
@@ -3,5 +3,5 @@ import { type Simplify } from 'type-fest';
3
3
  export type MergedOfValidators<Validators1 extends Partial<Readonly<Record<Keys, Validator>>>, Validators2 extends Partial<Readonly<Record<Keys, Validator>>>, Keys extends string = Extract<keyof Validators1 | keyof Validators2, string>> = Simplify<{
4
4
  readonly [K in Keys]: undefined extends Validators1[K] ? undefined extends Validators2[K] ? never : Validators2[K] : undefined extends Validators2[K] ? Validators1[K] : MergedOfValidator<NonNullable<Validators1[K]>, NonNullable<Validators2[K]>>;
5
5
  }>;
6
- export type MergedOfValidator<Validator1 extends Validator, Validator2 extends Validator> = Validator1 extends Validator<infer V, infer E1, infer P, infer C> ? Validator2 extends Validator<V, infer E2, P, C> ? Validator<V, E1 | E2, P, C> : never : never;
6
+ export type MergedOfValidator<Validator1 extends Validator, Validator2 extends Validator> = Validator1 extends Validator<infer V, infer E1, infer P, infer C1> ? Validator2 extends Validator<V, infer E2, P, infer C2> ? Validator<V, E1 | E2, P, C1 & C2> : never : never;
7
7
  export declare function mergeValidators<Validators1 extends Partial<Readonly<Record<Keys, Validator>>>, Validators2 extends Partial<Readonly<Record<Keys, Validator>>>, Keys extends string = Extract<keyof Validators1 | keyof Validators2, string>>(validators1: Validators1, validators2: Validators2): MergedOfValidators<Validators1, Validators2, Keys>;
@@ -7,12 +7,12 @@ $ tsup
7
7
  CLI Target: es6
8
8
  CJS Build start
9
9
  ESM Build start
10
- CJS dist/index.cjs 59.76 KB
11
- CJS ⚡️ Build success in 115ms
12
- ESM dist/index.js 55.85 KB
13
- ESM ⚡️ Build success in 122ms
10
+ ESM dist/index.js 57.40 KB
11
+ ESM ⚡️ Build success in 138ms
12
+ CJS dist/index.cjs 61.39 KB
13
+ CJS ⚡️ Build success in 139ms
14
14
  DTS Build start
15
- DTS ⚡️ Build success in 9788ms
16
- DTS dist/index.d.cts 37.25 KB
17
- DTS dist/index.d.ts 37.25 KB
18
- Done in 10.94s.
15
+ DTS ⚡️ Build success in 9840ms
16
+ DTS dist/index.d.cts 37.86 KB
17
+ DTS dist/index.d.ts 37.86 KB
18
+ Done in 10.97s.
@@ -1,3 +1,3 @@
1
1
  yarn run v1.22.22
2
2
  $ tsc
3
- Done in 7.58s.
3
+ Done in 7.62s.
@@ -336,8 +336,8 @@ export function trimmingStringAdapter<
336
336
 
337
337
  export function listAdapter<
338
338
  E,
339
- ValuePath extends string,
340
- Context,
339
+ ValuePath extends string = string,
340
+ Context = unknown,
341
341
  >() {
342
342
  return new FieldAdapterBuilder<readonly E[], readonly E[], never, ValuePath, Context>(
343
343
  annotatedIdentityConverter<readonly E[], ValuePath, Context>(false),
@@ -35,6 +35,7 @@ import {
35
35
  type ReadonlyDeep,
36
36
  type SimplifyDeep,
37
37
  type StringKeyOf,
38
+ type UnionToIntersection,
38
39
  type ValueOf,
39
40
  } from 'type-fest'
40
41
  import {
@@ -45,6 +46,7 @@ import {
45
46
  UnreliableFieldConversionType,
46
47
  } from 'types/field_converters'
47
48
  import {
49
+ type ContextOfFieldAdapter,
48
50
  type ErrorOfFieldAdapter,
49
51
  type FieldAdapter,
50
52
  type ToOfFieldAdapter,
@@ -102,6 +104,20 @@ export type ValuePathsToAdaptersOf<
102
104
  }
103
105
  : never
104
106
 
107
+ export type ContextOf<TypePathsToAdapters extends Partial<Readonly<Record<string, FieldAdapter>>>> =
108
+ UnionToIntersection<
109
+ | {
110
+ readonly [
111
+ K in keyof TypePathsToAdapters
112
+ ]: TypePathsToAdapters[K] extends undefined ? undefined
113
+ // ignore unspecified values
114
+ : unknown extends ContextOfFieldAdapter<NonNullable<TypePathsToAdapters[K]>> ? never
115
+ : ContextOfFieldAdapter<NonNullable<TypePathsToAdapters[K]>>
116
+ }[keyof TypePathsToAdapters]
117
+ // ensure we have at least one thing to intersect (can end up with a `never` context otherwise)
118
+ | {}
119
+ >
120
+
105
121
  export abstract class FormModel<
106
122
  T extends Type,
107
123
  ValueToTypePaths extends Readonly<Record<string, string>>,
@@ -109,7 +125,7 @@ export abstract class FormModel<
109
125
  FlattenedValuesOfType<T, '*'>,
110
126
  ContextType
111
127
  >,
112
- ContextType = {},
128
+ ContextType = ContextOf<TypePathsToAdapters>,
113
129
  ValuePathsToAdapters extends ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths> = ValuePathsToAdaptersOf<
114
130
  TypePathsToAdapters,
115
131
  ValueToTypePaths
@@ -131,7 +147,6 @@ export abstract class FormModel<
131
147
  ) {
132
148
  this.value = mobxCopy(type, value)
133
149
  this.flattenedTypeDefs = flattenTypesOfType(type)
134
- const contextValue = this.toContext(value)
135
150
  // pre-populate field overrides for consistent behavior when default information is overwritten
136
151
  // then returned to
137
152
  const conversions = flattenValueTo(
@@ -140,11 +155,14 @@ export abstract class FormModel<
140
155
  () => {},
141
156
  (
142
157
  _t: StrictTypeDef,
143
- value: AnyValueType,
158
+ fieldValue: AnyValueType,
144
159
  _setter,
145
160
  typePath,
146
161
  valuePath,
147
162
  ): AnnotatedFieldConversion<FieldOverride> | undefined => {
163
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
164
+ const contextValue = this.toContext(value, valuePath as keyof ValuePathsToAdapters)
165
+
148
166
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
149
167
  const adapter = this.adapters[typePath as keyof TypePathsToAdapters]
150
168
  if (adapter == null) {
@@ -159,7 +177,7 @@ export abstract class FormModel<
159
177
  return
160
178
  }
161
179
  // cannot call this.context yet as the "this" pointer has not been fully created
162
- return convert(value, valuePath, contextValue)
180
+ return convert(fieldValue, valuePath, contextValue)
163
181
  },
164
182
  )
165
183
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -168,12 +186,10 @@ export abstract class FormModel<
168
186
  }) as FlattenedFieldOverrides<ValuePathsToAdapters>
169
187
  }
170
188
 
171
- @computed.struct
172
- get context() {
173
- return this.toContext(this.value)
174
- }
175
-
176
- protected abstract toContext(value: ValueOfType<ReadonlyTypeOfType<T>>): ContextType
189
+ protected abstract toContext(
190
+ value: ValueOfType<ReadonlyTypeOfType<T>>,
191
+ valuePath: keyof ValuePathsToAdapters,
192
+ ): ContextType
177
193
 
178
194
  @computed
179
195
  get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>> {
@@ -247,6 +263,7 @@ export abstract class FormModel<
247
263
  const accessor = this.getAccessorForValuePath(valuePath)
248
264
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
249
265
  const fieldTypeDef = this.flattenedTypeDefs[typePath as string]
266
+ const context = this.toContext(this.value, valuePath)
250
267
  const {
251
268
  value,
252
269
  required,
@@ -258,14 +275,14 @@ export abstract class FormModel<
258
275
  ? mobxCopy(
259
276
  fieldTypeDef,
260
277
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
261
- create(valuePath as string, this.context),
278
+ create(valuePath as string, context),
262
279
  )
263
280
  // fake values can't be copied
264
281
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
265
- : create(valuePath as string, this.context),
282
+ : create(valuePath as string, context),
266
283
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
267
284
  valuePath as string,
268
- this.context,
285
+ context,
269
286
  )
270
287
  const error = this.errors[valuePath]
271
288
  return {
@@ -351,7 +368,9 @@ export abstract class FormModel<
351
368
  : elementAdapter.create(
352
369
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
353
370
  elementTypePath as string,
354
- this.context,
371
+ // TODO what can we use for the value path here?
372
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
373
+ this.toContext(this.value, valuePath as unknown as keyof ValuePathsToAdapters),
355
374
  )
356
375
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
357
376
  const originalList: any[] = accessor.value
@@ -496,7 +515,7 @@ export abstract class FormModel<
496
515
  assertExists(revert, 'setting value not supported {}', valuePath)
497
516
 
498
517
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
499
- const conversion = revert(value, valuePath as any, this.context)
518
+ const conversion = revert(value, valuePath as any, this.toContext(this.value, valuePath))
500
519
  const accessor = this.getAccessorForValuePath(valuePath)
501
520
  return runInAction(() => {
502
521
  this.fieldOverrides[valuePath] = [value]
@@ -539,10 +558,12 @@ export abstract class FormModel<
539
558
  convert,
540
559
  create,
541
560
  } = adapter
542
- const value = create(valuePath, this.context)
561
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
562
+ const context = this.toContext(this.value, valuePath as unknown as keyof ValuePathsToAdapters)
563
+ const value = create(valuePath, context)
543
564
  const {
544
565
  value: displayValue,
545
- } = convert(value, valuePath, this.context)
566
+ } = convert(value, valuePath, context)
546
567
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
547
568
  const key = valuePath as unknown as keyof ValuePathsToAdapters
548
569
  runInAction(() => {
@@ -577,16 +598,18 @@ export abstract class FormModel<
577
598
  } = this.getAdapterForValuePath(valuePath)
578
599
  const fieldOverride = this.fieldOverrides[valuePath]
579
600
  const accessor = this.getAccessorForValuePath(valuePath)
601
+ const context = this.toContext(this.value, valuePath)
602
+
580
603
  const {
581
604
  value: storedValue,
582
605
  } = convert(
583
606
  accessor != null
584
607
  ? accessor.value
585
608
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
586
- : create(valuePath as string, this.context),
609
+ : create(valuePath as string, context),
587
610
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
588
611
  valuePath as string,
589
- this.context,
612
+ context,
590
613
  )
591
614
  const value = fieldOverride != null
592
615
  ? fieldOverride[0]
@@ -596,13 +619,13 @@ export abstract class FormModel<
596
619
  if (ignoreDefaultValue) {
597
620
  const {
598
621
  value: defaultDisplayValue,
599
- } = convert(create(valuePath, this.context), valuePath, this.context)
622
+ } = convert(create(valuePath, context), valuePath, context)
600
623
  if (defaultDisplayValue === value) {
601
624
  return true
602
625
  }
603
626
  }
604
627
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
605
- const conversion = revert(value, valuePath as string, this.context)
628
+ const conversion = revert(value, valuePath as string, context)
606
629
  return runInAction(() => {
607
630
  switch (conversion.type) {
608
631
  case UnreliableFieldConversionType.Failure:
@@ -653,16 +676,18 @@ export abstract class FormModel<
653
676
  return success
654
677
  }
655
678
  const fieldOverride = this.fieldOverrides[adapterPath]
679
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
680
+ const context = this.toContext(this.value, valuePath as keyof ValuePathsToAdapters)
656
681
  const {
657
682
  value: storedValue,
658
- } = convert(accessor.value, valuePath, this.context)
683
+ } = convert(accessor.value, valuePath, context)
659
684
  const value = fieldOverride != null
660
685
  ? fieldOverride[0]
661
686
  : storedValue
662
687
  // TODO more nuanced comparison
663
688
  const dirty = fieldOverride != null && fieldOverride[0] !== storedValue
664
689
 
665
- const conversion = revert(value, valuePath, this.context)
690
+ const conversion = revert(value, valuePath, context)
666
691
  switch (conversion.type) {
667
692
  case UnreliableFieldConversionType.Failure:
668
693
  this.errors[adapterPath] = conversion.error
@@ -28,7 +28,7 @@ type MergedOfFieldAdapterWithValidator<
28
28
  V extends Validator | undefined,
29
29
  > = undefined extends V ? A
30
30
  : A extends FieldAdapter<infer From, infer To, infer E1, infer P1, infer C1>
31
- ? V extends Validator<From, infer E2, infer P2, infer C2> ? FieldAdapter<From, To, E1 | E2, P1 | P2, C1 | C2>
31
+ ? V extends Validator<From, infer E2, infer P2, infer C2> ? FieldAdapter<From, To, E1 | E2, P1 | P2, C1 & C2>
32
32
  : never
33
33
  : never
34
34
 
@@ -13,7 +13,9 @@ import {
13
13
  type ValueOfType,
14
14
  type ValueToTypePathsOfType,
15
15
  } from '@strictly/define'
16
- import { type FieldAdapter } from 'core/mobx/field_adapter'
16
+ import {
17
+ type FieldAdapter,
18
+ } from 'core/mobx/field_adapter'
17
19
  import {
18
20
  adapterFromTwoWayConverter,
19
21
  identityAdapter,
@@ -27,7 +29,9 @@ import { IntegerToStringConverter } from 'field_converters/integer_to_string_con
27
29
  import { NullableToBooleanConverter } from 'field_converters/nullable_to_boolean_converter'
28
30
  import { SelectDiscriminatedUnionConverter } from 'field_converters/select_value_type_converter'
29
31
  import { prototypingFieldValueFactory } from 'field_value_factories/prototyping_field_value_factory'
30
- import { type Simplify } from 'type-fest'
32
+ import {
33
+ type Simplify,
34
+ } from 'type-fest'
31
35
  import { type Field } from 'types/field'
32
36
  import {
33
37
  UnreliableFieldConversionType,
@@ -54,9 +58,13 @@ class TestFormModel<
54
58
  {}
55
59
  >,
56
60
  > extends FormModel<T, ValueToTypePaths, TypePathsToAdapters, {}> {
57
- override toContext() {
61
+ override toContext(
62
+ value: ValueOfType<T>,
63
+ valuePath: keyof ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths>,
64
+ ) {
58
65
  return {
59
- ctx: true,
66
+ value,
67
+ valuePath,
60
68
  }
61
69
  }
62
70
  }
@@ -740,10 +748,10 @@ describe('all', function () {
740
748
 
741
749
  // no longer passes context, but will pass context eventually again
742
750
  describe('passes context', function () {
743
- let contextCopy: number[]
751
+ let contextCopy: string
744
752
  beforeEach(function () {
745
753
  integerToStringAdapter.revert.mockImplementationOnce(function (_value, _path, context) {
746
- contextCopy = { ...context }
754
+ contextCopy = JSON.stringify(context)
747
755
  return {
748
756
  type: UnreliableFieldConversionType.Success,
749
757
  value: 1,
@@ -751,7 +759,7 @@ describe('all', function () {
751
759
  })
752
760
  })
753
761
 
754
- it('supplies the full, previous context when converting', function () {
762
+ it('supplies the context when converting', function () {
755
763
  model.setFieldValueAndValidate('$.2', '4')
756
764
 
757
765
  expect(integerToStringAdapter.revert).toHaveBeenCalledOnce()
@@ -759,14 +767,23 @@ describe('all', function () {
759
767
  '4',
760
768
  '$.2',
761
769
  {
762
- ctx: true,
770
+ // the supplied value isn't a copy, so it will be the model value, even
771
+ // if the value has since changed
772
+ value: model.value,
773
+ valuePath: '$.2',
763
774
  },
764
775
  )
765
776
  })
766
777
 
767
- it('supplies the context', function () {
768
- expect(contextCopy).toEqual({
769
- ctx: true,
778
+ it('supplies the correct context value at the time it is being checked', function () {
779
+ // the copy will show the supplied value however
780
+ expect(JSON.parse(contextCopy)).toEqual({
781
+ value: [
782
+ 1,
783
+ 3,
784
+ 7,
785
+ ],
786
+ valuePath: '$.2',
770
787
  })
771
788
  })
772
789
  })
@@ -6,7 +6,7 @@ import {
6
6
  } from './form_model'
7
7
 
8
8
  /**
9
- * Used to extract the supported value paths from a presenter
9
+ * Used to extract the supported value paths from a model
10
10
  */
11
11
  export type ValuePathsOfModel<
12
12
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -22,7 +22,7 @@ export type ValuePathsOfModel<
22
22
 
23
23
  /**
24
24
  * Used to extract the render type (so the value that is passed to the view) of a given value path
25
- * from a presenter
25
+ * from a model
26
26
  */
27
27
  export type ToValueOfModelValuePath<
28
28
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -38,9 +38,9 @@ export type ToValueOfModelValuePath<
38
38
  : never
39
39
 
40
40
  /**
41
- * Extracts the form fields from the presenter. The recommended way is to
41
+ * Extracts the form fields from a form model. The recommended way is to
42
42
  * define the form fields explicitly and use that type to enforce the types
43
- * of your converters, but generating the FormFields from your presenter
43
+ * of your converters, but generating the FormFields from your model
44
44
  * is less typing, albeit at the cost of potentially getting type errors
45
45
  * reported a long way away from the source
46
46
  */