@strictly/react-form 0.0.18 → 0.0.20

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 (37) hide show
  1. package/.out/core/mobx/form_model.d.ts +12 -9
  2. package/.out/core/mobx/form_model.js +103 -137
  3. package/.out/core/mobx/hooks.js +3 -4
  4. package/.out/core/mobx/merge_field_adapters_with_two_way_converter.d.ts +1 -1
  5. package/.out/core/mobx/merge_field_adapters_with_validators.js +5 -1
  6. package/.out/core/mobx/specs/form_model.tests.js +28 -23
  7. package/.out/mantine/create_fields_view.d.ts +2 -1
  8. package/.out/mantine/hooks.d.ts +6 -5
  9. package/.out/mantine/specs/checkbox_hooks.stories.d.ts +5 -2
  10. package/.out/mantine/specs/checkbox_hooks.stories.js +3 -2
  11. package/.out/mantine/specs/text_input_hooks.stories.d.ts +3 -2
  12. package/.out/mantine/specs/text_input_hooks.stories.js +3 -2
  13. package/.out/mantine/types.d.ts +3 -3
  14. package/.out/tsconfig.tsbuildinfo +1 -1
  15. package/.out/util/partial.d.ts +5 -2
  16. package/.out/util/specs/partial.tests.d.ts +1 -0
  17. package/.out/util/specs/partial.tests.js +8 -0
  18. package/.turbo/turbo-build.log +7 -7
  19. package/.turbo/turbo-check-types.log +2 -2
  20. package/.turbo/turbo-release$colon$exports.log +1 -1
  21. package/core/mobx/form_model.ts +95 -157
  22. package/core/mobx/hooks.tsx +6 -5
  23. package/core/mobx/merge_field_adapters_with_two_way_converter.ts +2 -1
  24. package/core/mobx/merge_field_adapters_with_validators.ts +1 -1
  25. package/core/mobx/specs/form_model.tests.ts +39 -27
  26. package/dist/index.cjs +93 -139
  27. package/dist/index.d.cts +28 -21
  28. package/dist/index.d.ts +28 -21
  29. package/dist/index.js +92 -139
  30. package/mantine/create_fields_view.tsx +8 -4
  31. package/mantine/hooks.tsx +23 -15
  32. package/mantine/specs/checkbox_hooks.stories.tsx +7 -1
  33. package/mantine/specs/text_input_hooks.stories.tsx +8 -1
  34. package/mantine/types.ts +12 -4
  35. package/package.json +12 -12
  36. package/util/partial.tsx +8 -1
  37. package/util/specs/partial.tests.tsx +21 -0
@@ -14,8 +14,12 @@ type FieldOverride<V = any> = Maybe<V>;
14
14
  type FlattenedFieldOverrides<ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>> = {
15
15
  -readonly [K in keyof ValuePathsToAdapters]?: FieldOverride<ToOfFieldAdapter<ValuePathsToAdapters[K]>>;
16
16
  };
17
- type FlattenedErrors<ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>> = {
18
- -readonly [K in keyof ValuePathsToAdapters]?: ErrorOfFieldAdapter<ValuePathsToAdapters[K]>;
17
+ export declare enum Validation {
18
+ Changed = 1,
19
+ Always = 2
20
+ }
21
+ type FlattenedValidation<ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>> = {
22
+ -readonly [K in keyof ValuePathsToAdapters]?: Validation;
19
23
  };
20
24
  export type ValuePathsToAdaptersOf<TypePathsToAdapters extends Partial<Readonly<Record<string, FieldAdapter>>>, ValuePathsToTypePaths extends Readonly<Record<string, string>>> = keyof TypePathsToAdapters extends ValueOf<ValuePathsToTypePaths> ? {
21
25
  readonly [K in keyof ValuePathsToTypePaths as unknown extends TypePathsToAdapters[ValuePathsToTypePaths[K]] ? never : K]: NonNullable<TypePathsToAdapters[ValuePathsToTypePaths[K]]>;
@@ -26,13 +30,13 @@ export type ContextOf<TypePathsToAdapters extends Partial<Readonly<Record<string
26
30
  export type FormMode = 'edit' | 'create';
27
31
  export 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>> {
28
32
  readonly type: T;
29
- private readonly originalValue;
30
33
  protected readonly adapters: TypePathsToAdapters;
31
34
  protected readonly mode: FormMode;
32
35
  accessor value: MobxValueOfType<T>;
33
36
  accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>;
34
- accessor errors: FlattenedErrors<ValuePathsToAdapters>;
37
+ accessor validation: FlattenedValidation<ValuePathsToAdapters>;
35
38
  private readonly flattenedTypeDefs;
39
+ private readonly originalValues;
36
40
  constructor(type: T, originalValue: ValueOfType<ReadonlyTypeOfType<T>>, adapters: TypePathsToAdapters, mode: FormMode);
37
41
  protected abstract toContext(value: ValueOfType<ReadonlyTypeOfType<T>>, valuePath: keyof ValuePathsToAdapters): ContextType;
38
42
  get forceMutableFields(): boolean;
@@ -45,16 +49,15 @@ export declare abstract class FormModel<T extends Type, ValueToTypePaths extends
45
49
  private maybeGetAdapterForValuePath;
46
50
  private getAdapterForValuePath;
47
51
  typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K];
48
- setFieldValueAndValidate<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>): boolean;
49
- setFieldValue<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>): boolean;
52
+ setFieldValue<K extends keyof ValuePathsToAdapters>(valuePath: K, value: ToOfFieldAdapter<ValuePathsToAdapters[K]>, validation?: Validation | undefined | null): boolean;
50
53
  addListItem<K extends keyof FlattenedListTypesOfType<T>>(valuePath: K, elementValue?: Maybe<ElementOfArray<FlattenedValuesOfType<T>[K]>>, index?: number): void;
51
54
  removeListItem<K extends keyof FlattenedListTypesOfType<T>>(...elementValuePaths: readonly `${K}.${number}`[]): void;
52
55
  private internalSetFieldValue;
53
56
  clearFieldError<K extends keyof ValuePathsToAdapters>(valuePath: K): void;
54
- clearFieldValue<K extends StringKeyOf<ValueToTypePaths>>(valuePath: K): void;
57
+ clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K): void;
55
58
  clearAll(value: ValueOfType<T>): void;
56
59
  isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean;
57
- validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, ignoreDefaultValue?: boolean): boolean;
58
- validateAll(force?: boolean): boolean;
60
+ validateField<K extends keyof ValuePathsToAdapters>(valuePath: K, validation?: Validation): boolean;
61
+ validateAll(validation?: Validation): boolean;
59
62
  }
60
63
  export {};
@@ -47,8 +47,13 @@ import { assertExists, assertExistsAndReturn, assertState, checkValidNumber, map
47
47
  import { flattenAccessorsOfType, flattenTypesOfType, flattenValuesOfType, flattenValueTo, jsonPathPop, mobxCopy, valuePathToTypePath, } from '@strictly/define';
48
48
  import { computed, observable, runInAction, } from 'mobx';
49
49
  import { UnreliableFieldConversionType, } from 'types/field_converters';
50
+ export var Validation;
51
+ (function (Validation) {
52
+ Validation[Validation["Changed"] = 1] = "Changed";
53
+ Validation[Validation["Always"] = 2] = "Always";
54
+ })(Validation || (Validation = {}));
50
55
  let FormModel = (() => {
51
- var _a, _FormModel_value_accessor_storage, _FormModel_fieldOverrides_accessor_storage, _FormModel_errors_accessor_storage;
56
+ var _a, _FormModel_value_accessor_storage, _FormModel_fieldOverrides_accessor_storage, _FormModel_validation_accessor_storage;
52
57
  var _b, _c, _d;
53
58
  let _instanceExtraInitializers = [];
54
59
  let _value_decorators;
@@ -57,9 +62,9 @@ let FormModel = (() => {
57
62
  let _fieldOverrides_decorators;
58
63
  let _fieldOverrides_initializers = [];
59
64
  let _fieldOverrides_extraInitializers = [];
60
- let _errors_decorators;
61
- let _errors_initializers = [];
62
- let _errors_extraInitializers = [];
65
+ let _validation_decorators;
66
+ let _validation_initializers = [];
67
+ let _validation_extraInitializers = [];
63
68
  let _get_fields_decorators;
64
69
  let _get_knownFields_decorators;
65
70
  let _get_accessors_decorators;
@@ -68,8 +73,8 @@ let FormModel = (() => {
68
73
  set value(value) { __classPrivateFieldSet(this, _FormModel_value_accessor_storage, value, "f"); }
69
74
  get fieldOverrides() { return __classPrivateFieldGet(this, _FormModel_fieldOverrides_accessor_storage, "f"); }
70
75
  set fieldOverrides(value) { __classPrivateFieldSet(this, _FormModel_fieldOverrides_accessor_storage, value, "f"); }
71
- get errors() { return __classPrivateFieldGet(this, _FormModel_errors_accessor_storage, "f"); }
72
- set errors(value) { __classPrivateFieldSet(this, _FormModel_errors_accessor_storage, value, "f"); }
76
+ get validation() { return __classPrivateFieldGet(this, _FormModel_validation_accessor_storage, "f"); }
77
+ set validation(value) { __classPrivateFieldSet(this, _FormModel_validation_accessor_storage, value, "f"); }
73
78
  constructor(type, originalValue, adapters, mode) {
74
79
  Object.defineProperty(this, "type", {
75
80
  enumerable: true,
@@ -77,12 +82,6 @@ let FormModel = (() => {
77
82
  writable: true,
78
83
  value: (__runInitializers(this, _instanceExtraInitializers), type)
79
84
  });
80
- Object.defineProperty(this, "originalValue", {
81
- enumerable: true,
82
- configurable: true,
83
- writable: true,
84
- value: originalValue
85
- });
86
85
  Object.defineProperty(this, "adapters", {
87
86
  enumerable: true,
88
87
  configurable: true,
@@ -97,13 +96,22 @@ let FormModel = (() => {
97
96
  });
98
97
  _FormModel_value_accessor_storage.set(this, __runInitializers(this, _value_initializers, void 0));
99
98
  _FormModel_fieldOverrides_accessor_storage.set(this, (__runInitializers(this, _value_extraInitializers), __runInitializers(this, _fieldOverrides_initializers, void 0)));
100
- _FormModel_errors_accessor_storage.set(this, (__runInitializers(this, _fieldOverrides_extraInitializers), __runInitializers(this, _errors_initializers, {})));
99
+ _FormModel_validation_accessor_storage.set(this, (__runInitializers(this, _fieldOverrides_extraInitializers), __runInitializers(this, _validation_initializers, {})));
101
100
  Object.defineProperty(this, "flattenedTypeDefs", {
102
101
  enumerable: true,
103
102
  configurable: true,
104
103
  writable: true,
105
- value: __runInitializers(this, _errors_extraInitializers)
104
+ value: __runInitializers(this, _validation_extraInitializers)
106
105
  });
106
+ // cannot be type safe
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ Object.defineProperty(this, "originalValues", {
109
+ enumerable: true,
110
+ configurable: true,
111
+ writable: true,
112
+ value: void 0
113
+ });
114
+ this.originalValues = flattenValuesOfType(type, originalValue);
107
115
  this.value = mobxCopy(type, originalValue);
108
116
  this.flattenedTypeDefs = flattenTypesOfType(type);
109
117
  // pre-populate field overrides for consistent behavior when default information is overwritten
@@ -187,24 +195,60 @@ let FormModel = (() => {
187
195
  // invalid path, which can happen
188
196
  return;
189
197
  }
190
- const { convert, create, } = adapter;
198
+ const { convert, create, revert, } = adapter;
191
199
  const fieldOverride = this.fieldOverrides[valuePath];
192
200
  const accessor = this.getAccessorForValuePath(valuePath);
193
201
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
194
202
  const fieldTypeDef = this.flattenedTypeDefs[typePath];
195
203
  const context = this.toContext(this.value, valuePath);
204
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
205
+ const defaultValue = create(valuePath, context);
196
206
  const { value, required, readonly, } = convert(accessor != null
197
207
  ? accessor.value
198
208
  : fieldTypeDef != null
199
- ? mobxCopy(fieldTypeDef,
200
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
201
- create(valuePath, context))
209
+ ? mobxCopy(fieldTypeDef, defaultValue)
202
210
  // fake values can't be copied
203
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
204
- : create(valuePath, context),
211
+ : defaultValue,
205
212
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
206
213
  valuePath, context);
207
- const error = this.errors[valuePath];
214
+ // const error = this.errors[valuePath]
215
+ let error = undefined;
216
+ const displayedValue = fieldOverride != null ? fieldOverride[0] : value;
217
+ const validation = this.validation[valuePath];
218
+ switch (validation) {
219
+ case undefined:
220
+ // skip validation
221
+ break;
222
+ case Validation.Changed:
223
+ if (revert != null) {
224
+ const originalValue = valuePath in this.originalValues
225
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
226
+ ? this.originalValues[valuePath]
227
+ : defaultValue;
228
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
229
+ const { value: originalDisplayedValue } = convert(originalValue, valuePath, context);
230
+ // TODO better comparisons, displayed values can still be complex
231
+ if (displayedValue !== originalDisplayedValue) {
232
+ const revertResult = revert(displayedValue, valuePath, context);
233
+ if ((revertResult === null || revertResult === void 0 ? void 0 : revertResult.type) === UnreliableFieldConversionType.Failure) {
234
+ ;
235
+ ({ error } = revertResult);
236
+ }
237
+ }
238
+ }
239
+ break;
240
+ case Validation.Always:
241
+ {
242
+ const revertResult = revert === null || revert === void 0 ? void 0 : revert(displayedValue, valuePath, context);
243
+ if ((revertResult === null || revertResult === void 0 ? void 0 : revertResult.type) === UnreliableFieldConversionType.Failure) {
244
+ ;
245
+ ({ error } = revertResult);
246
+ }
247
+ }
248
+ break;
249
+ default:
250
+ throw new UnreachableError(validation);
251
+ }
208
252
  return {
209
253
  value: fieldOverride != null ? fieldOverride[0] : value,
210
254
  error,
@@ -233,11 +277,8 @@ let FormModel = (() => {
233
277
  typePath(valuePath) {
234
278
  return valuePathToTypePath(this.type, valuePath, true);
235
279
  }
236
- setFieldValueAndValidate(valuePath, value) {
237
- return this.internalSetFieldValue(valuePath, value, true);
238
- }
239
- setFieldValue(valuePath, value) {
240
- return this.internalSetFieldValue(valuePath, value, false);
280
+ setFieldValue(valuePath, value, validation = this.validation[valuePath]) {
281
+ return this.internalSetFieldValue(valuePath, value, validation);
241
282
  }
242
283
  addListItem(valuePath,
243
284
  // TODO can this type be simplified?
@@ -300,9 +341,9 @@ let FormModel = (() => {
300
341
  const fieldOverride = this.fieldOverrides[fromJsonPath];
301
342
  delete this.fieldOverrides[fromJsonPath];
302
343
  this.fieldOverrides[toJsonPath] = fieldOverride;
303
- const error = this.errors[fromJsonPath];
304
- delete this.errors[fromJsonPath];
305
- this.errors[toJsonPath] = error;
344
+ const validation = this.validation[fromJsonPath];
345
+ delete this.validation[fromJsonPath];
346
+ this.validation[toJsonPath] = validation;
306
347
  });
307
348
  accessor.set(newList);
308
349
  // delete any value overrides so the new list isn't shadowed
@@ -354,9 +395,9 @@ let FormModel = (() => {
354
395
  const fieldOverride = this.fieldOverrides[fromJsonPath];
355
396
  delete this.fieldOverrides[fromJsonPath];
356
397
  this.fieldOverrides[toJsonPath] = fieldOverride;
357
- const error = this.errors[fromJsonPath];
358
- delete this.errors[fromJsonPath];
359
- this.errors[toJsonPath] = error;
398
+ const validation = this.validation[fromJsonPath];
399
+ delete this.validation[fromJsonPath];
400
+ this.validation[toJsonPath] = validation;
360
401
  });
361
402
  accessor.set(newList);
362
403
  // delete any value overrides so the new list isn't shadowed
@@ -365,7 +406,7 @@ let FormModel = (() => {
365
406
  });
366
407
  });
367
408
  }
368
- internalSetFieldValue(valuePath, value, displayValidation) {
409
+ internalSetFieldValue(valuePath, value, validation) {
369
410
  const { revert } = this.getAdapterForValuePath(valuePath);
370
411
  assertExists(revert, 'setting value not supported {}', valuePath);
371
412
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
@@ -373,17 +414,19 @@ let FormModel = (() => {
373
414
  const accessor = this.getAccessorForValuePath(valuePath);
374
415
  return runInAction(() => {
375
416
  this.fieldOverrides[valuePath] = [value];
417
+ if (validation != null) {
418
+ this.validation[valuePath] = validation;
419
+ }
420
+ else {
421
+ delete this.validation[valuePath];
422
+ }
376
423
  switch (conversion.type) {
377
424
  case UnreliableFieldConversionType.Failure:
378
- if (displayValidation) {
379
- this.errors[valuePath] = conversion.error;
380
- }
381
425
  if (conversion.value != null && accessor != null) {
382
426
  accessor.set(conversion.value[0]);
383
427
  }
384
428
  return false;
385
429
  case UnreliableFieldConversionType.Success:
386
- delete this.errors[valuePath];
387
430
  accessor === null || accessor === void 0 ? void 0 : accessor.set(conversion.value);
388
431
  return true;
389
432
  default:
@@ -395,7 +438,7 @@ let FormModel = (() => {
395
438
  const fieldOverride = this.fieldOverrides[valuePath];
396
439
  if (fieldOverride != null) {
397
440
  runInAction(() => {
398
- delete this.errors[valuePath];
441
+ delete this.validation[valuePath];
399
442
  });
400
443
  }
401
444
  }
@@ -415,11 +458,12 @@ let FormModel = (() => {
415
458
  const key = valuePath;
416
459
  runInAction(() => {
417
460
  this.fieldOverrides[key] = [displayValue];
461
+ delete this.validation[key];
418
462
  });
419
463
  }
420
464
  clearAll(value) {
421
465
  runInAction(() => {
422
- this.errors = {};
466
+ this.validation = {};
423
467
  // TODO this isn't correct, should reload from value
424
468
  this.fieldOverrides = {};
425
469
  this.value = mobxCopy(this.type, value);
@@ -431,121 +475,43 @@ let FormModel = (() => {
431
475
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
432
476
  return keys.has(valuePath);
433
477
  }
434
- validateField(valuePath, ignoreDefaultValue = false) {
435
- const { convert, revert, create, } = this.getAdapterForValuePath(valuePath);
436
- const fieldOverride = this.fieldOverrides[valuePath];
437
- const accessor = this.getAccessorForValuePath(valuePath);
438
- const context = this.toContext(this.value, valuePath);
439
- const { value: storedValue, } = convert(accessor != null
440
- ? accessor.value
441
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
442
- : create(valuePath, context),
443
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
444
- valuePath, context);
445
- const value = fieldOverride != null
446
- ? fieldOverride[0]
447
- : storedValue;
448
- const dirty = storedValue !== value;
449
- assertExists(revert, 'changing field directly not supported {}', valuePath);
450
- if (ignoreDefaultValue) {
451
- const { value: defaultDisplayValue, } = convert(create(valuePath, context), valuePath, context);
452
- if (defaultDisplayValue === value) {
453
- return true;
454
- }
455
- }
456
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
457
- const conversion = revert(value, valuePath, context);
458
- return runInAction(() => {
459
- switch (conversion.type) {
460
- case UnreliableFieldConversionType.Failure:
461
- this.errors[valuePath] = conversion.error;
462
- if (conversion.value != null && accessor != null && dirty) {
463
- accessor.set(conversion.value[0]);
464
- }
465
- return false;
466
- case UnreliableFieldConversionType.Success:
467
- delete this.errors[valuePath];
468
- if (accessor != null && dirty) {
469
- accessor.set(conversion.value);
470
- }
471
- return true;
472
- default:
473
- throw new UnreachableError(conversion);
474
- }
478
+ validateField(valuePath, validation) {
479
+ var _b;
480
+ if (validation === void 0) { validation = Math.max(this.mode === 'create' ? Validation.Always : Validation.Changed, (_b = this.validation[valuePath]) !== null && _b !== void 0 ? _b : Validation.Changed); }
481
+ runInAction(() => {
482
+ this.validation[valuePath] = validation;
475
483
  });
484
+ return this.fields[valuePath].error == null;
476
485
  }
477
- validateAll(force = this.mode === 'create') {
478
- // sort keys shortest to longest so parent changes don't overwrite child changes
479
- const accessors = toArray(this.accessors).toSorted(function ([a], [b]) {
480
- return a.length - b.length;
481
- });
482
- const flattenedOriginalValues = flattenValuesOfType(this.type, this.originalValue);
483
- return runInAction(() => {
484
- return accessors.reduce((success, [valuePath, accessor,]) => {
485
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
486
- const adapterPath = valuePath;
487
- const adapter = this.maybeGetAdapterForValuePath(adapterPath);
488
- if (adapter == null) {
489
- // no adapter == there should be nothing specified for this field
490
- return success;
491
- }
492
- const { convert, revert, } = adapter;
493
- if (revert == null) {
494
- // no convert method means this field is immutable
495
- return success;
496
- }
497
- const fieldOverride = this.fieldOverrides[adapterPath];
486
+ validateAll(validation = this.mode === 'create' ? Validation.Always : Validation.Changed) {
487
+ const accessors = toArray(this.accessors);
488
+ runInAction(() => {
489
+ accessors.forEach(([valuePath]) => {
498
490
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
499
- const context = this.toContext(this.value, valuePath);
500
- const { value: storedValue, } = convert(accessor.value, valuePath, context);
501
- const value = fieldOverride != null
502
- ? fieldOverride[0]
503
- : storedValue;
504
- // TODO customizable comparisons
505
- const dirty = fieldOverride != null && fieldOverride[0] !== storedValue;
506
- const needsValidation = force
507
- || !(valuePath in flattenedOriginalValues)
508
- || storedValue !== convert(flattenedOriginalValues[valuePath], valuePath,
509
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
510
- this.toContext(this.originalValue, valuePath)).value;
511
- if (needsValidation) {
512
- const conversion = revert(value, valuePath, context);
513
- switch (conversion.type) {
514
- case UnreliableFieldConversionType.Failure:
515
- this.errors[adapterPath] = conversion.error;
516
- if (conversion.value != null && dirty) {
517
- accessor.set(conversion.value[0]);
518
- }
519
- return false;
520
- case UnreliableFieldConversionType.Success:
521
- if (dirty) {
522
- accessor.set(conversion.value);
523
- }
524
- delete this.errors[adapterPath];
525
- return success;
526
- default:
527
- throw new UnreachableError(conversion);
528
- }
529
- }
530
- return success;
531
- }, true);
491
+ this.validation[valuePath] = validation;
492
+ });
493
+ });
494
+ return accessors.every(([valuePath]) => {
495
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
496
+ const field = this.fields[valuePath];
497
+ return (field === null || field === void 0 ? void 0 : field.error) == null;
532
498
  });
533
499
  }
534
500
  },
535
501
  _FormModel_value_accessor_storage = new WeakMap(),
536
502
  _FormModel_fieldOverrides_accessor_storage = new WeakMap(),
537
- _FormModel_errors_accessor_storage = new WeakMap(),
503
+ _FormModel_validation_accessor_storage = new WeakMap(),
538
504
  (() => {
539
505
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
540
506
  _value_decorators = [(_b = observable).ref.bind(_b)];
541
507
  _fieldOverrides_decorators = [(_c = observable).shallow.bind(_c)];
542
- _errors_decorators = [(_d = observable).shallow.bind(_d)];
508
+ _validation_decorators = [(_d = observable).shallow.bind(_d)];
543
509
  _get_fields_decorators = [computed];
544
510
  _get_knownFields_decorators = [computed];
545
511
  _get_accessors_decorators = [computed];
546
512
  __esDecorate(_a, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
547
513
  __esDecorate(_a, null, _fieldOverrides_decorators, { kind: "accessor", name: "fieldOverrides", static: false, private: false, access: { has: obj => "fieldOverrides" in obj, get: obj => obj.fieldOverrides, set: (obj, value) => { obj.fieldOverrides = value; } }, metadata: _metadata }, _fieldOverrides_initializers, _fieldOverrides_extraInitializers);
548
- __esDecorate(_a, null, _errors_decorators, { kind: "accessor", name: "errors", static: false, private: false, access: { has: obj => "errors" in obj, get: obj => obj.errors, set: (obj, value) => { obj.errors = value; } }, metadata: _metadata }, _errors_initializers, _errors_extraInitializers);
514
+ __esDecorate(_a, null, _validation_decorators, { kind: "accessor", name: "validation", static: false, private: false, access: { has: obj => "validation" in obj, get: obj => obj.validation, set: (obj, value) => { obj.validation = value; } }, metadata: _metadata }, _validation_initializers, _validation_extraInitializers);
549
515
  __esDecorate(_a, null, _get_fields_decorators, { kind: "getter", name: "fields", static: false, private: false, access: { has: obj => "fields" in obj, get: obj => obj.fields }, metadata: _metadata }, null, _instanceExtraInitializers);
550
516
  __esDecorate(_a, null, _get_knownFields_decorators, { kind: "getter", name: "knownFields", static: false, private: false, access: { has: obj => "knownFields" in obj, get: obj => obj.knownFields }, metadata: _metadata }, null, _instanceExtraInitializers);
551
517
  __esDecorate(_a, null, _get_accessors_decorators, { kind: "getter", name: "accessors", static: false, private: false, access: { has: obj => "accessors" in obj, get: obj => obj.accessors }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -1,9 +1,8 @@
1
1
  import { useCallback, } from 'react';
2
2
  export function useDefaultMobxFormHooks(model, { onValidFieldSubmit, onValidFormSubmit, } = {}) {
3
3
  const onFieldValueChange = useCallback(function (path, value) {
4
- // TODO do in one action
5
- model.clearFieldError(path);
6
- model.setFieldValue(path, value);
4
+ // clear any validation
5
+ model.setFieldValue(path, value, null);
7
6
  }, [model]);
8
7
  const onFieldSubmit = useCallback(function (valuePath) {
9
8
  if (model.validateField(valuePath)) {
@@ -20,7 +19,7 @@ export function useDefaultMobxFormHooks(model, { onValidFieldSubmit, onValidForm
20
19
  // TODO debounce?
21
20
  setTimeout(function () {
22
21
  if (model.isValuePathActive(path)) {
23
- model.validateField(path, true);
22
+ model.validateField(path);
24
23
  }
25
24
  }, 100);
26
25
  }, [model]);
@@ -9,5 +9,5 @@ type ValuePathsOfFieldAdapters<FieldAdapters extends Readonly<Record<string, Fie
9
9
  type TosOfFieldAdapters<FieldAdapters extends Readonly<Record<string, FieldAdapter>>> = {
10
10
  [K in keyof FieldAdapters]: ToOfFieldAdapter<FieldAdapters[K]>;
11
11
  }[keyof FieldAdapters];
12
- export declare function mergeFieldAdaptersWithTwoWayConverter<FieldAdapters extends Readonly<Record<string, FieldAdapter>>, E, Context>(fieldAdapters: FieldAdapters, converter: TwoWayFieldConverter<TosOfFieldAdapters<FieldAdapters>, TosOfFieldAdapters<FieldAdapters>, E, ValuePathsOfFieldAdapters<FieldAdapters>, Context>): MergedOfFieldAdaptersWithTwoWayConverter<FieldAdapters, E, Context>;
12
+ export declare function mergeFieldAdaptersWithTwoWayConverter<FieldAdapters extends Readonly<Record<string, FieldAdapter>>, E, Context, P extends ValuePathsOfFieldAdapters<FieldAdapters>>(fieldAdapters: FieldAdapters, converter: TwoWayFieldConverter<TosOfFieldAdapters<FieldAdapters>, TosOfFieldAdapters<FieldAdapters>, E, P, Context>): MergedOfFieldAdaptersWithTwoWayConverter<FieldAdapters, E, Context>;
13
13
  export {};
@@ -35,7 +35,11 @@ export function mergeAdaptersWithValidators(adapters, validators) {
35
35
  readonly: readonly1 || readonly2,
36
36
  };
37
37
  }
38
- acc[key] = Object.assign(Object.assign({}, adapter), { convert, revert: adapter.revert && revert });
38
+ acc[key] = {
39
+ create: adapter.create.bind(adapter),
40
+ convert,
41
+ revert: adapter.revert && revert,
42
+ };
39
43
  return acc;
40
44
  }, {});
41
45
  }
@@ -1,7 +1,7 @@
1
1
  import { expectDefinedAndReturn } from '@strictly/base';
2
2
  import { booleanType, flattenValidatorsOfValidatingType, list, nullType, numberType, object, record, stringType, union, } from '@strictly/define';
3
3
  import { adapterFromTwoWayConverter, identityAdapter, } from 'core/mobx/field_adapter_builder';
4
- import { FormModel, } from 'core/mobx/form_model';
4
+ import { FormModel, Validation, } from 'core/mobx/form_model';
5
5
  import { mergeAdaptersWithValidators } from 'core/mobx/merge_field_adapters_with_validators';
6
6
  import { IntegerToStringConverter } from 'field_converters/integer_to_string_converter';
7
7
  import { NullableToBooleanConverter } from 'field_converters/nullable_to_boolean_converter';
@@ -19,6 +19,9 @@ class TestFormModel extends FormModel {
19
19
  valuePath,
20
20
  };
21
21
  }
22
+ setFieldValueAndValidate(valuePath, value) {
23
+ this.setFieldValue(valuePath, value, Validation.Always);
24
+ }
22
25
  }
23
26
  describe('all', function () {
24
27
  const integerToStringAdapter = createMockedAdapter(originalIntegerToStringAdapter);
@@ -336,10 +339,10 @@ describe('all', function () {
336
339
  });
337
340
  describe('conversion succeeds, but validation fails', function () {
338
341
  const newValue = -1;
339
- const errorCode = 65;
342
+ const errorCode = IS_NAN_ERROR;
340
343
  beforeEach(function () {
341
344
  var _a;
342
- (_a = integerToStringAdapter.revert) === null || _a === void 0 ? void 0 : _a.mockReturnValueOnce({
345
+ (_a = integerToStringAdapter.revert) === null || _a === void 0 ? void 0 : _a.mockReturnValue({
343
346
  type: UnreliableFieldConversionType.Failure,
344
347
  error: errorCode,
345
348
  value: [newValue],
@@ -527,9 +530,10 @@ describe('all', function () {
527
530
  describe('addListItem', function () {
528
531
  describe('adds default to start of the list', function () {
529
532
  beforeEach(function () {
530
- model.errors['$.0'] = 0;
531
- model.errors['$.1'] = 1;
532
- model.errors['$.2'] = 2;
533
+ model.setFieldValue('$.0', 'x');
534
+ model.setFieldValue('$.1', '3');
535
+ model.setFieldValue('$.2', 'z');
536
+ model.validateAll();
533
537
  model.addListItem('$', null, 0);
534
538
  });
535
539
  it('adds the list item to the underlying value', function () {
@@ -547,7 +551,7 @@ describe('all', function () {
547
551
  ],
548
552
  [
549
553
  '$.1',
550
- '1',
554
+ 'x',
551
555
  ],
552
556
  [
553
557
  '$.2',
@@ -555,7 +559,7 @@ describe('all', function () {
555
559
  ],
556
560
  [
557
561
  '$.3',
558
- '7',
562
+ 'z',
559
563
  ],
560
564
  ])('it reports the value of field %s as %s', function (path, fieldValue) {
561
565
  var _a;
@@ -568,15 +572,15 @@ describe('all', function () {
568
572
  ],
569
573
  [
570
574
  '$.1',
571
- 0,
575
+ IS_NAN_ERROR,
572
576
  ],
573
577
  [
574
578
  '$.2',
575
- 1,
579
+ undefined,
576
580
  ],
577
581
  [
578
582
  '$.3',
579
- 2,
583
+ IS_NAN_ERROR,
580
584
  ],
581
585
  ])('it reports the error of field %s', function (path, error) {
582
586
  var _a;
@@ -615,9 +619,10 @@ describe('all', function () {
615
619
  });
616
620
  describe('removeListItem', function () {
617
621
  beforeEach(function () {
618
- model.errors['$.0'] = 0;
619
- model.errors['$.1'] = 1;
620
- model.errors['$.2'] = 2;
622
+ model.setFieldValue('$.0', 'x');
623
+ model.setFieldValue('$.1', '3');
624
+ model.setFieldValue('$.2', 'z');
625
+ model.validateAll();
621
626
  });
622
627
  describe('remove first item', function () {
623
628
  beforeEach(function () {
@@ -633,11 +638,11 @@ describe('all', function () {
633
638
  expect(model.fields).toEqual({
634
639
  '$.0': expect.objectContaining({
635
640
  value: '3',
636
- error: 1,
641
+ error: undefined,
637
642
  }),
638
643
  '$.1': expect.objectContaining({
639
- value: '7',
640
- error: 2,
644
+ value: 'z',
645
+ error: IS_NAN_ERROR,
641
646
  }),
642
647
  });
643
648
  });
@@ -655,12 +660,12 @@ describe('all', function () {
655
660
  it('updates the field values and errors', function () {
656
661
  expect(model.fields).toEqual({
657
662
  '$.0': expect.objectContaining({
658
- value: '1',
659
- error: 0,
663
+ value: 'x',
664
+ error: IS_NAN_ERROR,
660
665
  }),
661
666
  '$.1': expect.objectContaining({
662
- value: '7',
663
- error: 2,
667
+ value: 'z',
668
+ error: IS_NAN_ERROR,
664
669
  }),
665
670
  });
666
671
  });
@@ -675,8 +680,8 @@ describe('all', function () {
675
680
  it('updates the field values and errors', function () {
676
681
  expect(model.fields).toEqual({
677
682
  '$.0': expect.objectContaining({
678
- value: '7',
679
- error: 2,
683
+ value: 'z',
684
+ error: IS_NAN_ERROR,
680
685
  }),
681
686
  });
682
687
  });
@@ -5,8 +5,9 @@ import type { AllFieldsOfFields } from 'types/all_fields_of_fields';
5
5
  import type { Fields } from 'types/field';
6
6
  import type { SubFormFields } from 'types/sub_form_fields';
7
7
  import type { MantineFieldComponent } from './types';
8
+ export type SubPathsOf<ValuePath extends string, SubFormValuePath extends string> = SubFormValuePath extends StringConcatOf<ValuePath, infer Postfix> ? `$${Postfix}` : never;
8
9
  export type CallbackMapper<ValuePath extends string> = {
9
- <Cb extends (...args: any[]) => any>(cb: Cb): Parameters<Cb> extends [infer SubFormValuePath extends string, ...(infer Rest)] ? SubFormValuePath extends StringConcatOf<ValuePath, infer Postfix> ? (valuePath: `$${Postfix}`, ...rest: Rest) => ReturnType<Cb> : never : never;
10
+ <Cb extends (...args: any[]) => any>(cb: Cb): Parameters<Cb> extends [infer SubFormValuePath extends string, ...(infer Rest)] ? (valuePath: SubPathsOf<ValuePath, SubFormValuePath>, ...rest: Rest) => ReturnType<Cb> : never;
10
11
  };
11
12
  export type FieldsView<ValuePath extends string = string, C extends ComponentType<any> = ComponentType<any>> = {
12
13
  Component: C;