@strictly/react-form 0.0.16 → 0.0.17

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.
@@ -7,12 +7,12 @@ $ tsup
7
7
  CLI Target: es6
8
8
  CJS Build start
9
9
  ESM Build start
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
10
+ CJS dist/index.cjs 62.28 KB
11
+ CJS ⚡️ Build success in 117ms
12
+ ESM dist/index.js 58.27 KB
13
+ ESM ⚡️ Build success in 140ms
14
14
  DTS Build start
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.
15
+ DTS ⚡️ Build success in 9749ms
16
+ DTS dist/index.d.cts 38.08 KB
17
+ DTS dist/index.d.ts 38.08 KB
18
+ Done in 10.88s.
@@ -1,3 +1,3 @@
1
1
  yarn run v1.22.22
2
2
  $ tsc
3
- Done in 7.62s.
3
+ Done in 7.55s.
@@ -26,6 +26,7 @@ import {
26
26
  type ValueOfType,
27
27
  valuePathToTypePath,
28
28
  } from '@strictly/define'
29
+ import { type FormMode } from 'core/props'
29
30
  import {
30
31
  computed,
31
32
  observable,
@@ -142,10 +143,11 @@ export abstract class FormModel<
142
143
 
143
144
  constructor(
144
145
  readonly type: T,
145
- value: ValueOfType<ReadonlyTypeOfType<T>>,
146
+ private readonly originalValue: ValueOfType<ReadonlyTypeOfType<T>>,
146
147
  protected readonly adapters: TypePathsToAdapters,
148
+ protected readonly mode: FormMode,
147
149
  ) {
148
- this.value = mobxCopy(type, value)
150
+ this.value = mobxCopy(type, originalValue)
149
151
  this.flattenedTypeDefs = flattenTypesOfType(type)
150
152
  // pre-populate field overrides for consistent behavior when default information is overwritten
151
153
  // then returned to
@@ -161,7 +163,7 @@ export abstract class FormModel<
161
163
  valuePath,
162
164
  ): AnnotatedFieldConversion<FieldOverride> | undefined => {
163
165
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
164
- const contextValue = this.toContext(value, valuePath as keyof ValuePathsToAdapters)
166
+ const contextValue = this.toContext(originalValue, valuePath as keyof ValuePathsToAdapters)
165
167
 
166
168
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
167
169
  const adapter = this.adapters[typePath as keyof TypePathsToAdapters]
@@ -191,6 +193,17 @@ export abstract class FormModel<
191
193
  valuePath: keyof ValuePathsToAdapters,
192
194
  ): ContextType
193
195
 
196
+ get forceMutableFields() {
197
+ switch (this.mode) {
198
+ case 'create':
199
+ return true
200
+ case 'edit':
201
+ return false
202
+ default:
203
+ return this.mode satisfies never
204
+ }
205
+ }
206
+
194
207
  @computed
195
208
  get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>> {
196
209
  return new Proxy<SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>>(
@@ -288,7 +301,7 @@ export abstract class FormModel<
288
301
  return {
289
302
  value: fieldOverride != null ? fieldOverride[0] : value,
290
303
  error,
291
- readonly,
304
+ readonly: readonly && !this.forceMutableFields,
292
305
  required,
293
306
  }
294
307
  }
@@ -646,11 +659,14 @@ export abstract class FormModel<
646
659
  })
647
660
  }
648
661
 
649
- validateAll(): boolean {
662
+ validateAll(force: boolean = this.mode === 'create'): boolean {
650
663
  // sort keys shortest to longest so parent changes don't overwrite child changes
651
664
  const accessors = toArray(this.accessors).toSorted(function ([a], [b]) {
652
665
  return a.length - b.length
653
666
  })
667
+
668
+ const flattenedOriginalValues = flattenValuesOfType(this.type, this.originalValue)
669
+
654
670
  return runInAction(() => {
655
671
  return accessors.reduce(
656
672
  (
@@ -684,26 +700,36 @@ export abstract class FormModel<
684
700
  const value = fieldOverride != null
685
701
  ? fieldOverride[0]
686
702
  : storedValue
687
- // TODO more nuanced comparison
703
+ // TODO customizable comparisons
688
704
  const dirty = fieldOverride != null && fieldOverride[0] !== storedValue
689
-
690
- const conversion = revert(value, valuePath, context)
691
- switch (conversion.type) {
692
- case UnreliableFieldConversionType.Failure:
693
- this.errors[adapterPath] = conversion.error
694
- if (conversion.value != null && dirty) {
695
- accessor.set(conversion.value[0])
696
- }
697
- return false
698
- case UnreliableFieldConversionType.Success:
699
- if (dirty) {
700
- accessor.set(conversion.value)
701
- }
702
- delete this.errors[adapterPath]
703
- return success
704
- default:
705
- throw new UnreachableError(conversion)
705
+ const needsValidation = force
706
+ || !(valuePath in flattenedOriginalValues)
707
+ || storedValue !== convert(
708
+ flattenedOriginalValues[valuePath],
709
+ valuePath,
710
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
711
+ this.toContext(this.originalValue, valuePath as keyof ValuePathsToAdapters),
712
+ ).value
713
+ if (needsValidation) {
714
+ const conversion = revert(value, valuePath, context)
715
+ switch (conversion.type) {
716
+ case UnreliableFieldConversionType.Failure:
717
+ this.errors[adapterPath] = conversion.error
718
+ if (conversion.value != null && dirty) {
719
+ accessor.set(conversion.value[0])
720
+ }
721
+ return false
722
+ case UnreliableFieldConversionType.Success:
723
+ if (dirty) {
724
+ accessor.set(conversion.value)
725
+ }
726
+ delete this.errors[adapterPath]
727
+ return success
728
+ default:
729
+ throw new UnreachableError(conversion)
730
+ }
706
731
  }
732
+ return success
707
733
  },
708
734
  true,
709
735
  )
@@ -2,6 +2,7 @@ import { expectDefinedAndReturn } from '@strictly/base'
2
2
  import {
3
3
  booleanType,
4
4
  type FlattenedValuesOfType,
5
+ flattenValidatorsOfValidatingType,
5
6
  list,
6
7
  nullType,
7
8
  numberType,
@@ -25,6 +26,7 @@ import {
25
26
  FormModel,
26
27
  type ValuePathsToAdaptersOf,
27
28
  } from 'core/mobx/form_model'
29
+ import { mergeAdaptersWithValidators } from 'core/mobx/merge_field_adapters_with_validators'
28
30
  import { IntegerToStringConverter } from 'field_converters/integer_to_string_converter'
29
31
  import { NullableToBooleanConverter } from 'field_converters/nullable_to_boolean_converter'
30
32
  import { SelectDiscriminatedUnionConverter } from 'field_converters/select_value_type_converter'
@@ -203,6 +205,7 @@ describe('all', function () {
203
205
  typeDef,
204
206
  originalValue,
205
207
  adapters,
208
+ 'create',
206
209
  )
207
210
  })
208
211
 
@@ -263,6 +266,7 @@ describe('all', function () {
263
266
  typeDef,
264
267
  originalValue,
265
268
  adapters,
269
+ 'create',
266
270
  )
267
271
  })
268
272
 
@@ -304,6 +308,7 @@ describe('all', function () {
304
308
  typeDef,
305
309
  value,
306
310
  adapters,
311
+ 'create',
307
312
  )
308
313
  })
309
314
 
@@ -382,6 +387,7 @@ describe('all', function () {
382
387
  typeDef,
383
388
  value,
384
389
  converters,
390
+ 'create',
385
391
  )
386
392
  })
387
393
 
@@ -452,6 +458,7 @@ describe('all', function () {
452
458
  typeDef,
453
459
  value,
454
460
  converters,
461
+ 'create',
455
462
  )
456
463
  })
457
464
 
@@ -517,6 +524,7 @@ describe('all', function () {
517
524
  typeDef,
518
525
  originalValue,
519
526
  adapters,
527
+ 'create',
520
528
  )
521
529
  })
522
530
 
@@ -643,6 +651,7 @@ describe('all', function () {
643
651
  typeDef,
644
652
  originalValue,
645
653
  converters,
654
+ 'create',
646
655
  )
647
656
  })
648
657
 
@@ -993,6 +1002,7 @@ describe('all', function () {
993
1002
  type,
994
1003
  originalValue,
995
1004
  adapters,
1005
+ 'create',
996
1006
  )
997
1007
  })
998
1008
 
@@ -1061,6 +1071,7 @@ describe('all', function () {
1061
1071
  a: 1,
1062
1072
  },
1063
1073
  adapters,
1074
+ 'create',
1064
1075
  )
1065
1076
  it.each([
1066
1077
  [
@@ -1093,6 +1104,7 @@ describe('all', function () {
1093
1104
  b: false,
1094
1105
  },
1095
1106
  adapters,
1107
+ 'create',
1096
1108
  )
1097
1109
  it.each([
1098
1110
  [
@@ -1142,6 +1154,7 @@ describe('all', function () {
1142
1154
  typeDef,
1143
1155
  originalValue,
1144
1156
  converters,
1157
+ 'create',
1145
1158
  )
1146
1159
  })
1147
1160
 
@@ -1167,5 +1180,95 @@ describe('all', function () {
1167
1180
  })
1168
1181
  })
1169
1182
  })
1183
+
1184
+ describe('interaction with create and edit modes', () => {
1185
+ const typeDef = object().readonlyField('n', numberType.enforce(n => n < 10 ? 'err' : null))
1186
+ const adapters = mergeAdaptersWithValidators(
1187
+ {
1188
+ $: identityAdapter({ n: 0 }),
1189
+ '$.n': integerToStringAdapter,
1190
+ } as const,
1191
+ flattenValidatorsOfValidatingType(typeDef),
1192
+ )
1193
+ type JsonPaths = {
1194
+ $: '$',
1195
+ '$.n': '$.n',
1196
+ }
1197
+ let originalValue: ValueOfType<typeof typeDef>
1198
+ beforeEach(() => {
1199
+ originalValue = {
1200
+ n: 1,
1201
+ }
1202
+ })
1203
+ describe('create mode', () => {
1204
+ let model: FormModel<
1205
+ typeof typeDef,
1206
+ JsonPaths,
1207
+ typeof adapters
1208
+ >
1209
+ beforeEach(() => {
1210
+ model = new TestFormModel<
1211
+ typeof typeDef,
1212
+ JsonPaths,
1213
+ typeof adapters
1214
+ >(
1215
+ typeDef,
1216
+ originalValue,
1217
+ adapters,
1218
+ 'create',
1219
+ )
1220
+ })
1221
+
1222
+ it('makes the field editable', () => {
1223
+ expect(model.fields['$.n'].readonly).toBeFalsy()
1224
+ })
1225
+
1226
+ it('fails validation', () => {
1227
+ expect(model.validateAll()).toBeFalsy()
1228
+ })
1229
+
1230
+ it('passes validation with valid data', () => {
1231
+ model.setFieldValue('$.n', '10')
1232
+ expect(model.validateAll()).toBeTruthy()
1233
+ })
1234
+ })
1235
+ describe('edit model', () => {
1236
+ let model: FormModel<
1237
+ typeof typeDef,
1238
+ JsonPaths,
1239
+ typeof adapters
1240
+ >
1241
+ beforeEach(function () {
1242
+ model = new TestFormModel<
1243
+ typeof typeDef,
1244
+ JsonPaths,
1245
+ typeof adapters
1246
+ >(
1247
+ typeDef,
1248
+ originalValue,
1249
+ adapters,
1250
+ 'edit',
1251
+ )
1252
+ })
1253
+
1254
+ it('respects the field being readonly', () => {
1255
+ expect(model.fields['$.n'].readonly).toBeTruthy()
1256
+ })
1257
+
1258
+ it('validates successfully with clean, but invalid data', () => {
1259
+ expect(model.validateAll()).toBeTruthy()
1260
+ })
1261
+
1262
+ it('fails validation with invalid, dirty data', () => {
1263
+ model.setFieldValue('$.n', '2')
1264
+ expect(model.validateAll()).toBeFalsy()
1265
+ })
1266
+
1267
+ it('passes validation with valid, dirty data', () => {
1268
+ model.setFieldValue('$.n', '10')
1269
+ expect(model.validateAll()).toBeTruthy()
1270
+ })
1271
+ })
1272
+ })
1170
1273
  })
1171
1274
  })
package/core/props.ts CHANGED
@@ -14,8 +14,12 @@ export type FieldsViewProps<F extends Fields> = {
14
14
  onFieldSubmit?(this: void, key: keyof F): boolean | void,
15
15
  }
16
16
 
17
+ export type FormMode = 'edit' | 'create'
18
+
17
19
  export type FormProps<O> = {
18
20
  value: O,
19
21
 
20
22
  onValueChange: (value: O) => void,
23
+
24
+ mode: FormMode,
21
25
  }
package/dist/index.cjs CHANGED
@@ -359,15 +359,17 @@ var import_mobx = require("mobx");
359
359
  var _accessors_dec, _knownFields_dec, _fields_dec, _errors_dec, _fieldOverrides_dec, _value_dec, _init, _value, _fieldOverrides, _errors;
360
360
  _value_dec = [import_mobx.observable.ref], _fieldOverrides_dec = [import_mobx.observable.shallow], _errors_dec = [import_mobx.observable.shallow], _fields_dec = [import_mobx.computed], _knownFields_dec = [import_mobx.computed], _accessors_dec = [import_mobx.computed];
361
361
  var FormModel = class {
362
- constructor(type, value, adapters) {
362
+ constructor(type, originalValue, adapters, mode) {
363
363
  this.type = type;
364
+ this.originalValue = originalValue;
364
365
  this.adapters = adapters;
366
+ this.mode = mode;
365
367
  __runInitializers(_init, 5, this);
366
368
  __privateAdd(this, _value, __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
367
369
  __privateAdd(this, _fieldOverrides, __runInitializers(_init, 12, this)), __runInitializers(_init, 15, this);
368
370
  __privateAdd(this, _errors, __runInitializers(_init, 16, this, {})), __runInitializers(_init, 19, this);
369
371
  __publicField(this, "flattenedTypeDefs");
370
- this.value = (0, import_define.mobxCopy)(type, value);
372
+ this.value = (0, import_define.mobxCopy)(type, originalValue);
371
373
  this.flattenedTypeDefs = (0, import_define.flattenTypesOfType)(type);
372
374
  const conversions = (0, import_define.flattenValueTo)(
373
375
  type,
@@ -375,7 +377,7 @@ var FormModel = class {
375
377
  () => {
376
378
  },
377
379
  (_t, fieldValue, _setter, typePath, valuePath) => {
378
- const contextValue = this.toContext(value, valuePath);
380
+ const contextValue = this.toContext(originalValue, valuePath);
379
381
  const adapter2 = this.adapters[typePath];
380
382
  if (adapter2 == null) {
381
383
  return;
@@ -394,6 +396,16 @@ var FormModel = class {
394
396
  return v && [v.value];
395
397
  });
396
398
  }
399
+ get forceMutableFields() {
400
+ switch (this.mode) {
401
+ case "create":
402
+ return true;
403
+ case "edit":
404
+ return false;
405
+ default:
406
+ return this.mode;
407
+ }
408
+ }
397
409
  get fields() {
398
410
  return new Proxy(
399
411
  this.knownFields,
@@ -472,7 +484,7 @@ var FormModel = class {
472
484
  return {
473
485
  value: fieldOverride != null ? fieldOverride[0] : value,
474
486
  error,
475
- readonly,
487
+ readonly: readonly && !this.forceMutableFields,
476
488
  required
477
489
  };
478
490
  }
@@ -754,10 +766,11 @@ var FormModel = class {
754
766
  }
755
767
  });
756
768
  }
757
- validateAll() {
769
+ validateAll(force = this.mode === "create") {
758
770
  const accessors = (0, import_base2.toArray)(this.accessors).toSorted(function([a], [b]) {
759
771
  return a.length - b.length;
760
772
  });
773
+ const flattenedOriginalValues = (0, import_define.flattenValuesOfType)(this.type, this.originalValue);
761
774
  return (0, import_mobx.runInAction)(() => {
762
775
  return accessors.reduce(
763
776
  (success, [
@@ -783,23 +796,32 @@ var FormModel = class {
783
796
  } = convert(accessor.value, valuePath, context);
784
797
  const value = fieldOverride != null ? fieldOverride[0] : storedValue;
785
798
  const dirty = fieldOverride != null && fieldOverride[0] !== storedValue;
786
- const conversion = revert(value, valuePath, context);
787
- switch (conversion.type) {
788
- case 1 /* Failure */:
789
- this.errors[adapterPath] = conversion.error;
790
- if (conversion.value != null && dirty) {
791
- accessor.set(conversion.value[0]);
792
- }
793
- return false;
794
- case 0 /* Success */:
795
- if (dirty) {
796
- accessor.set(conversion.value);
797
- }
798
- delete this.errors[adapterPath];
799
- return success;
800
- default:
801
- throw new import_base2.UnreachableError(conversion);
799
+ const needsValidation = force || !(valuePath in flattenedOriginalValues) || storedValue !== convert(
800
+ flattenedOriginalValues[valuePath],
801
+ valuePath,
802
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
803
+ this.toContext(this.originalValue, valuePath)
804
+ ).value;
805
+ if (needsValidation) {
806
+ const conversion = revert(value, valuePath, context);
807
+ switch (conversion.type) {
808
+ case 1 /* Failure */:
809
+ this.errors[adapterPath] = conversion.error;
810
+ if (conversion.value != null && dirty) {
811
+ accessor.set(conversion.value[0]);
812
+ }
813
+ return false;
814
+ case 0 /* Success */:
815
+ if (dirty) {
816
+ accessor.set(conversion.value);
817
+ }
818
+ delete this.errors[adapterPath];
819
+ return success;
820
+ default:
821
+ throw new import_base2.UnreachableError(conversion);
822
+ }
802
823
  }
824
+ return success;
803
825
  },
804
826
  true
805
827
  );
@@ -1753,7 +1775,7 @@ function useMantineFormFields({
1753
1775
  function() {
1754
1776
  return new MantineFormImpl(fields);
1755
1777
  },
1756
- // handled separately below
1778
+ // fields handled separately below
1757
1779
  // eslint-disable-next-line react-hooks/exhaustive-deps
1758
1780
  []
1759
1781
  );
package/dist/index.d.cts CHANGED
@@ -97,6 +97,20 @@ type FormFieldsOfFieldAdapters<ValuePathsToTypePaths extends Readonly<Record<str
97
97
  };
98
98
  type FormFieldOfFieldAdapter<F extends FieldAdapter | undefined> = F extends FieldAdapter<infer _From, infer To, infer E> ? Field<To, E> : never;
99
99
 
100
+ type FieldsViewProps<F extends Fields> = {
101
+ fields: F;
102
+ onFieldValueChange<K extends keyof F>(this: void, key: K, value: F[K]['value']): void;
103
+ onFieldFocus?(this: void, key: keyof F): void;
104
+ onFieldBlur?(this: void, key: keyof F): void;
105
+ onFieldSubmit?(this: void, key: keyof F): boolean | void;
106
+ };
107
+ type FormMode = 'edit' | 'create';
108
+ type FormProps<O> = {
109
+ value: O;
110
+ onValueChange: (value: O) => void;
111
+ mode: FormMode;
112
+ };
113
+
100
114
  type FlattenedListTypesOfType<T extends Type> = FlattenedListTypesOfTypes<SimplifyDeep<FlattenedTypesOfType<T, null>>>;
101
115
  type FlattenedListTypesOfTypes<T extends Readonly<Record<string, Type>>> = {
102
116
  [K in keyof T as T[K]['definition'] extends ListTypeDef ? K : never]: T[K];
@@ -123,13 +137,16 @@ type ContextOf<TypePathsToAdapters extends Partial<Readonly<Record<string, Field
123
137
  }[keyof TypePathsToAdapters] | {}>;
124
138
  declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readonly<Record<string, string>>, TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<FlattenedValuesOfType<T, '*'>, ContextType>, ContextType = ContextOf<TypePathsToAdapters>, ValuePathsToAdapters extends ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths> = ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths>> {
125
139
  readonly type: T;
140
+ private readonly originalValue;
126
141
  protected readonly adapters: TypePathsToAdapters;
142
+ protected readonly mode: FormMode;
127
143
  accessor value: MobxValueOfType<T>;
128
144
  accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>;
129
145
  accessor errors: FlattenedErrors<ValuePathsToAdapters>;
130
146
  private readonly flattenedTypeDefs;
131
- constructor(type: T, value: ValueOfType<ReadonlyTypeOfType<T>>, adapters: TypePathsToAdapters);
147
+ constructor(type: T, originalValue: ValueOfType<ReadonlyTypeOfType<T>>, adapters: TypePathsToAdapters, mode: FormMode);
132
148
  protected abstract toContext(value: ValueOfType<ReadonlyTypeOfType<T>>, valuePath: keyof ValuePathsToAdapters): ContextType;
149
+ get forceMutableFields(): boolean;
133
150
  get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>;
134
151
  private get knownFields();
135
152
  private maybeSynthesizeFieldByValuePath;
@@ -149,7 +166,7 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
149
166
  clearAll(value: ValueOfType<T>): void;
150
167
  isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
151
168
  validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, ignoreDefaultValue?: boolean): boolean;
152
- validateAll(): boolean;
169
+ validateAll(force?: boolean): boolean;
153
170
  }
154
171
 
155
172
  /**
@@ -207,18 +224,6 @@ type SubFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>, Type
207
224
  };
208
225
  declare function subFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>, TypePath extends string, TypePathsToValuePaths extends Record<TypePath, string>>(subAdapters: SubAdapters, parentTypePath: TypePath): SubFormFieldAdapters<SubAdapters, TypePath, TypePathsToValuePaths[TypePath]>;
209
226
 
210
- type FieldsViewProps<F extends Fields> = {
211
- fields: F;
212
- onFieldValueChange<K extends keyof F>(this: void, key: K, value: F[K]['value']): void;
213
- onFieldFocus?(this: void, key: keyof F): void;
214
- onFieldBlur?(this: void, key: keyof F): void;
215
- onFieldSubmit?(this: void, key: keyof F): boolean | void;
216
- };
217
- type FormProps<O> = {
218
- value: O;
219
- onValueChange: (value: O) => void;
220
- };
221
-
222
227
  declare class IntegerToStringConverter<E, ValuePath extends string, Context> implements TwoWayFieldConverter<number, string, E, ValuePath, Context> {
223
228
  private readonly isNanError;
224
229
  private readonly base;
@@ -445,4 +450,4 @@ declare function mergeValidators<Validators1 extends Partial<Readonly<Record<Key
445
450
 
446
451
  declare function Empty(): null;
447
452
 
448
- export { AbstractSelectValueTypeConverter, type AnnotatedFieldConversion, type AnnotatedFieldConverter, type Annotation, type ContextOf, type ContextOfFieldAdapter, DefaultErrorRenderer, Empty, type ErrorOfField, type ErrorOfFieldAdapter, type ErrorRenderer, type ErrorRendererProps, type Field, type FieldAdapter, type FieldAdaptersOfValues, type FieldValueFactory, type Fields, type FieldsViewProps, type FlattenedAdaptersOfFields, type FlattenedConvertedFieldsOf, type FlattenedTypePathsToAdaptersOf, type FormFieldsOfFieldAdapters, type FormFieldsOfModel, FormModel, type FormProps, type FromOfFieldAdapter, IntegerToStringConverter, type MergedOfFieldAdaptersWithTwoWayConverter, type MergedOfFieldAdaptersWithValidators, type MergedOfValidator, type MergedOfValidators, NullableToBooleanConverter, type PartialComponent, SelectDiscriminatedUnionConverter, SelectLiteralConverter, SelectStringConverter, type ToOfFieldAdapter, type ToValueOfModelValuePath, TrimmingStringConverter, type TwoWayFieldConverter, type TwoWayFieldConverterWithValueFactory, type UnreliableFieldConversion, UnreliableFieldConversionType, type UnreliableFieldConverter, type UnsafePartialComponent, type ValuePathOfFieldAdapter, type ValuePathsOfModel, type ValuePathsToAdaptersOf, adapter, adapterFromPrototype, adapterFromTwoWayConverter, createPartialComponent, createPartialObserverComponent, createSimplePartialComponent, createUnsafePartialObserverComponent, identityAdapter, listAdapter, mergeAdaptersWithValidators, mergeFieldAdaptersWithTwoWayConverter, mergeValidators, prototypingFieldValueFactory, subFormFieldAdapters, trimmingStringAdapter, useDefaultMobxFormHooks, useMantineFormFields, usePartialComponent, usePartialObserverComponent, validatingConverter };
453
+ export { AbstractSelectValueTypeConverter, type AnnotatedFieldConversion, type AnnotatedFieldConverter, type Annotation, type ContextOf, type ContextOfFieldAdapter, DefaultErrorRenderer, Empty, type ErrorOfField, type ErrorOfFieldAdapter, type ErrorRenderer, type ErrorRendererProps, type Field, type FieldAdapter, type FieldAdaptersOfValues, type FieldValueFactory, type Fields, type FieldsViewProps, type FlattenedAdaptersOfFields, type FlattenedConvertedFieldsOf, type FlattenedTypePathsToAdaptersOf, type FormFieldsOfFieldAdapters, type FormFieldsOfModel, type FormMode, FormModel, type FormProps, type FromOfFieldAdapter, IntegerToStringConverter, type MergedOfFieldAdaptersWithTwoWayConverter, type MergedOfFieldAdaptersWithValidators, type MergedOfValidator, type MergedOfValidators, NullableToBooleanConverter, type PartialComponent, SelectDiscriminatedUnionConverter, SelectLiteralConverter, SelectStringConverter, type ToOfFieldAdapter, type ToValueOfModelValuePath, TrimmingStringConverter, type TwoWayFieldConverter, type TwoWayFieldConverterWithValueFactory, type UnreliableFieldConversion, UnreliableFieldConversionType, type UnreliableFieldConverter, type UnsafePartialComponent, type ValuePathOfFieldAdapter, type ValuePathsOfModel, type ValuePathsToAdaptersOf, adapter, adapterFromPrototype, adapterFromTwoWayConverter, createPartialComponent, createPartialObserverComponent, createSimplePartialComponent, createUnsafePartialObserverComponent, identityAdapter, listAdapter, mergeAdaptersWithValidators, mergeFieldAdaptersWithTwoWayConverter, mergeValidators, prototypingFieldValueFactory, subFormFieldAdapters, trimmingStringAdapter, useDefaultMobxFormHooks, useMantineFormFields, usePartialComponent, usePartialObserverComponent, validatingConverter };
package/dist/index.d.ts CHANGED
@@ -97,6 +97,20 @@ type FormFieldsOfFieldAdapters<ValuePathsToTypePaths extends Readonly<Record<str
97
97
  };
98
98
  type FormFieldOfFieldAdapter<F extends FieldAdapter | undefined> = F extends FieldAdapter<infer _From, infer To, infer E> ? Field<To, E> : never;
99
99
 
100
+ type FieldsViewProps<F extends Fields> = {
101
+ fields: F;
102
+ onFieldValueChange<K extends keyof F>(this: void, key: K, value: F[K]['value']): void;
103
+ onFieldFocus?(this: void, key: keyof F): void;
104
+ onFieldBlur?(this: void, key: keyof F): void;
105
+ onFieldSubmit?(this: void, key: keyof F): boolean | void;
106
+ };
107
+ type FormMode = 'edit' | 'create';
108
+ type FormProps<O> = {
109
+ value: O;
110
+ onValueChange: (value: O) => void;
111
+ mode: FormMode;
112
+ };
113
+
100
114
  type FlattenedListTypesOfType<T extends Type> = FlattenedListTypesOfTypes<SimplifyDeep<FlattenedTypesOfType<T, null>>>;
101
115
  type FlattenedListTypesOfTypes<T extends Readonly<Record<string, Type>>> = {
102
116
  [K in keyof T as T[K]['definition'] extends ListTypeDef ? K : never]: T[K];
@@ -123,13 +137,16 @@ type ContextOf<TypePathsToAdapters extends Partial<Readonly<Record<string, Field
123
137
  }[keyof TypePathsToAdapters] | {}>;
124
138
  declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readonly<Record<string, string>>, TypePathsToAdapters extends FlattenedTypePathsToAdaptersOf<FlattenedValuesOfType<T, '*'>, ContextType>, ContextType = ContextOf<TypePathsToAdapters>, ValuePathsToAdapters extends ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths> = ValuePathsToAdaptersOf<TypePathsToAdapters, ValueToTypePaths>> {
125
139
  readonly type: T;
140
+ private readonly originalValue;
126
141
  protected readonly adapters: TypePathsToAdapters;
142
+ protected readonly mode: FormMode;
127
143
  accessor value: MobxValueOfType<T>;
128
144
  accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>;
129
145
  accessor errors: FlattenedErrors<ValuePathsToAdapters>;
130
146
  private readonly flattenedTypeDefs;
131
- constructor(type: T, value: ValueOfType<ReadonlyTypeOfType<T>>, adapters: TypePathsToAdapters);
147
+ constructor(type: T, originalValue: ValueOfType<ReadonlyTypeOfType<T>>, adapters: TypePathsToAdapters, mode: FormMode);
132
148
  protected abstract toContext(value: ValueOfType<ReadonlyTypeOfType<T>>, valuePath: keyof ValuePathsToAdapters): ContextType;
149
+ get forceMutableFields(): boolean;
133
150
  get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>;
134
151
  private get knownFields();
135
152
  private maybeSynthesizeFieldByValuePath;
@@ -149,7 +166,7 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
149
166
  clearAll(value: ValueOfType<T>): void;
150
167
  isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
151
168
  validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, ignoreDefaultValue?: boolean): boolean;
152
- validateAll(): boolean;
169
+ validateAll(force?: boolean): boolean;
153
170
  }
154
171
 
155
172
  /**
@@ -207,18 +224,6 @@ type SubFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>, Type
207
224
  };
208
225
  declare function subFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>, TypePath extends string, TypePathsToValuePaths extends Record<TypePath, string>>(subAdapters: SubAdapters, parentTypePath: TypePath): SubFormFieldAdapters<SubAdapters, TypePath, TypePathsToValuePaths[TypePath]>;
209
226
 
210
- type FieldsViewProps<F extends Fields> = {
211
- fields: F;
212
- onFieldValueChange<K extends keyof F>(this: void, key: K, value: F[K]['value']): void;
213
- onFieldFocus?(this: void, key: keyof F): void;
214
- onFieldBlur?(this: void, key: keyof F): void;
215
- onFieldSubmit?(this: void, key: keyof F): boolean | void;
216
- };
217
- type FormProps<O> = {
218
- value: O;
219
- onValueChange: (value: O) => void;
220
- };
221
-
222
227
  declare class IntegerToStringConverter<E, ValuePath extends string, Context> implements TwoWayFieldConverter<number, string, E, ValuePath, Context> {
223
228
  private readonly isNanError;
224
229
  private readonly base;
@@ -445,4 +450,4 @@ declare function mergeValidators<Validators1 extends Partial<Readonly<Record<Key
445
450
 
446
451
  declare function Empty(): null;
447
452
 
448
- export { AbstractSelectValueTypeConverter, type AnnotatedFieldConversion, type AnnotatedFieldConverter, type Annotation, type ContextOf, type ContextOfFieldAdapter, DefaultErrorRenderer, Empty, type ErrorOfField, type ErrorOfFieldAdapter, type ErrorRenderer, type ErrorRendererProps, type Field, type FieldAdapter, type FieldAdaptersOfValues, type FieldValueFactory, type Fields, type FieldsViewProps, type FlattenedAdaptersOfFields, type FlattenedConvertedFieldsOf, type FlattenedTypePathsToAdaptersOf, type FormFieldsOfFieldAdapters, type FormFieldsOfModel, FormModel, type FormProps, type FromOfFieldAdapter, IntegerToStringConverter, type MergedOfFieldAdaptersWithTwoWayConverter, type MergedOfFieldAdaptersWithValidators, type MergedOfValidator, type MergedOfValidators, NullableToBooleanConverter, type PartialComponent, SelectDiscriminatedUnionConverter, SelectLiteralConverter, SelectStringConverter, type ToOfFieldAdapter, type ToValueOfModelValuePath, TrimmingStringConverter, type TwoWayFieldConverter, type TwoWayFieldConverterWithValueFactory, type UnreliableFieldConversion, UnreliableFieldConversionType, type UnreliableFieldConverter, type UnsafePartialComponent, type ValuePathOfFieldAdapter, type ValuePathsOfModel, type ValuePathsToAdaptersOf, adapter, adapterFromPrototype, adapterFromTwoWayConverter, createPartialComponent, createPartialObserverComponent, createSimplePartialComponent, createUnsafePartialObserverComponent, identityAdapter, listAdapter, mergeAdaptersWithValidators, mergeFieldAdaptersWithTwoWayConverter, mergeValidators, prototypingFieldValueFactory, subFormFieldAdapters, trimmingStringAdapter, useDefaultMobxFormHooks, useMantineFormFields, usePartialComponent, usePartialObserverComponent, validatingConverter };
453
+ export { AbstractSelectValueTypeConverter, type AnnotatedFieldConversion, type AnnotatedFieldConverter, type Annotation, type ContextOf, type ContextOfFieldAdapter, DefaultErrorRenderer, Empty, type ErrorOfField, type ErrorOfFieldAdapter, type ErrorRenderer, type ErrorRendererProps, type Field, type FieldAdapter, type FieldAdaptersOfValues, type FieldValueFactory, type Fields, type FieldsViewProps, type FlattenedAdaptersOfFields, type FlattenedConvertedFieldsOf, type FlattenedTypePathsToAdaptersOf, type FormFieldsOfFieldAdapters, type FormFieldsOfModel, type FormMode, FormModel, type FormProps, type FromOfFieldAdapter, IntegerToStringConverter, type MergedOfFieldAdaptersWithTwoWayConverter, type MergedOfFieldAdaptersWithValidators, type MergedOfValidator, type MergedOfValidators, NullableToBooleanConverter, type PartialComponent, SelectDiscriminatedUnionConverter, SelectLiteralConverter, SelectStringConverter, type ToOfFieldAdapter, type ToValueOfModelValuePath, TrimmingStringConverter, type TwoWayFieldConverter, type TwoWayFieldConverterWithValueFactory, type UnreliableFieldConversion, UnreliableFieldConversionType, type UnreliableFieldConverter, type UnsafePartialComponent, type ValuePathOfFieldAdapter, type ValuePathsOfModel, type ValuePathsToAdaptersOf, adapter, adapterFromPrototype, adapterFromTwoWayConverter, createPartialComponent, createPartialObserverComponent, createSimplePartialComponent, createUnsafePartialObserverComponent, identityAdapter, listAdapter, mergeAdaptersWithValidators, mergeFieldAdaptersWithTwoWayConverter, mergeValidators, prototypingFieldValueFactory, subFormFieldAdapters, trimmingStringAdapter, useDefaultMobxFormHooks, useMantineFormFields, usePartialComponent, usePartialObserverComponent, validatingConverter };