@strictly/react-form 0.0.31 → 0.0.32

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.
@@ -1,8 +1,16 @@
1
1
  import '@mantine/core/styles.css'
2
2
  import { MantineProvider } from '@mantine/core'
3
3
  import { type Preview } from '@storybook/react'
4
+ import { configure } from 'mobx'
4
5
  import { StrictMode } from 'react'
5
6
 
7
+ // turn on all useful mobx warnings in storybook to try to catch bad behavior
8
+ configure({
9
+ enforceActions: 'observed',
10
+ observableRequiresReaction: true,
11
+ reactionRequiresObservable: true,
12
+ })
13
+
6
14
  const preview: Preview = {
7
15
  parameters: {
8
16
  controls: {
@@ -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 62.94 KB
11
- CJS ⚡️ Build success in 142ms
12
- ESM dist/index.js 58.83 KB
13
- ESM ⚡️ Build success in 148ms
10
+ CJS dist/index.cjs 64.18 KB
11
+ CJS ⚡️ Build success in 121ms
12
+ ESM dist/index.js 60.02 KB
13
+ ESM ⚡️ Build success in 126ms
14
14
  DTS Build start
15
- DTS ⚡️ Build success in 12298ms
16
- DTS dist/index.d.cts 38.21 KB
17
- DTS dist/index.d.ts 38.21 KB
18
- Done in 13.48s.
15
+ DTS ⚡️ Build success in 6938ms
16
+ DTS dist/index.d.cts 38.43 KB
17
+ DTS dist/index.d.ts 38.43 KB
18
+ Done in 8.16s.
@@ -1,3 +1,3 @@
1
1
  yarn run v1.22.22
2
2
  $ tsc -b
3
- Done in 0.41s.
3
+ Done in 0.37s.
@@ -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.
@@ -11,6 +11,7 @@ import {
11
11
  import {
12
12
  type Accessor,
13
13
  type AnyValueType,
14
+ copy,
14
15
  equals,
15
16
  flattenAccessorsOfType,
16
17
  type FlattenedValuesOfType,
@@ -27,6 +28,7 @@ import {
27
28
  valuePathToTypePath,
28
29
  } from '@strictly/define'
29
30
  import {
31
+ action,
30
32
  computed,
31
33
  observable,
32
34
  runInAction,
@@ -146,7 +148,7 @@ export abstract class FormModel<
146
148
  >,
147
149
  > {
148
150
  @observable.ref
149
- accessor value: MobxValueOfType<T>
151
+ private accessor observableValue: MobxValueOfType<T>
150
152
  @observable.shallow
151
153
  accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>
152
154
  @observable.shallow
@@ -170,13 +172,13 @@ export abstract class FormModel<
170
172
  protected readonly mode: FormMode,
171
173
  ) {
172
174
  this.originalValues = flattenValuesOfType<ReadonlyTypeOfType<T>>(type, originalValue, this.listIndicesToKeys)
173
- this.value = mobxCopy(type, originalValue)
175
+ this.observableValue = mobxCopy(type, originalValue)
174
176
  this.flattenedTypeDefs = flattenTypesOfType(type)
175
177
  // pre-populate field overrides for consistent behavior when default information is overwritten
176
178
  // then returned to
177
179
  const conversions = flattenValueTo(
178
180
  type,
179
- this.value,
181
+ originalValue,
180
182
  () => {},
181
183
  (
182
184
  _t: StrictTypeDef,
@@ -228,6 +230,12 @@ export abstract class FormModel<
228
230
  }
229
231
  }
230
232
 
233
+ @computed
234
+ get value(): ValueOfType<ReadonlyTypeOfType<T>> {
235
+ // copy and strip out the mobx so this computed will fire every time anything changes
236
+ return copy(this.type, this.observableValue)
237
+ }
238
+
231
239
  @computed
232
240
  get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>> {
233
241
  return new Proxy<SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>>(
@@ -252,7 +260,7 @@ export abstract class FormModel<
252
260
  private get knownFields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>> {
253
261
  return flattenValueTo(
254
262
  this.type,
255
- this.value,
263
+ this.observableValue,
256
264
  () => {},
257
265
  // TODO swap these to valuePath, typePath in flatten
258
266
  (_t: StrictTypeDef, _v: AnyValueType, _setter, typePath, valuePath): Field | undefined => {
@@ -302,7 +310,7 @@ export abstract class FormModel<
302
310
  const accessor = this.getAccessorForValuePath(valuePath)
303
311
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
304
312
  const fieldTypeDef = this.flattenedTypeDefs[typePath as string]
305
- const context = this.toContext(this.value, valuePath)
313
+ const context = this.toContext(this.observableValue, valuePath)
306
314
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
307
315
  const defaultValue = create(valuePath as string, context)
308
316
 
@@ -414,9 +422,9 @@ export abstract class FormModel<
414
422
  get accessors(): Readonly<Record<string, Accessor>> {
415
423
  return flattenAccessorsOfType<T, Readonly<Record<string, Accessor>>>(
416
424
  this.type,
417
- this.value,
425
+ this.observableValue,
418
426
  (value: ValueOfType<T>): void => {
419
- this.value = mobxCopy(this.type, value)
427
+ this.observableValue = mobxCopy(this.type, value)
420
428
  },
421
429
  this.listIndicesToKeys,
422
430
  )
@@ -448,13 +456,14 @@ export abstract class FormModel<
448
456
  @computed
449
457
  get valueChanged() {
450
458
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
451
- return !equals(this.type, this.value, this.originalValue as ValueOfType<T>)
459
+ return !equals(this.type, this.observableValue, this.originalValue as ValueOfType<T>)
452
460
  }
453
461
 
454
462
  typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K] {
455
463
  return valuePathToTypePath<ValueToTypePaths, K>(this.type, valuePath, true)
456
464
  }
457
465
 
466
+ @action
458
467
  setFieldValue<K extends keyof ValuePathsToAdapters>(
459
468
  valuePath: K,
460
469
  value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
@@ -490,7 +499,7 @@ export abstract class FormModel<
490
499
  elementTypePath as string,
491
500
  // TODO what can we use for the value path here?
492
501
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
493
- this.toContext(this.value, valuePath as unknown as keyof ValuePathsToAdapters),
502
+ this.toContext(this.observableValue, valuePath as unknown as keyof ValuePathsToAdapters),
494
503
  )
495
504
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
496
505
  const originalList: any[] = accessor.value
@@ -560,7 +569,7 @@ export abstract class FormModel<
560
569
  assertExists(revert, 'setting value not supported {}', valuePath)
561
570
 
562
571
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
563
- const conversion = revert(value, valuePath as any, this.toContext(this.value, valuePath))
572
+ const conversion = revert(value, valuePath as any, this.toContext(this.observableValue, valuePath))
564
573
  const accessor = this.getAccessorForValuePath(valuePath)
565
574
  return runInAction(() => {
566
575
  this.fieldOverrides[valuePath] = [value]
@@ -623,7 +632,7 @@ export abstract class FormModel<
623
632
  create,
624
633
  } = adapter
625
634
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
626
- const context = this.toContext(this.value, valuePath as unknown as keyof ValuePathsToAdapters)
635
+ const context = this.toContext(this.observableValue, valuePath as unknown as keyof ValuePathsToAdapters)
627
636
  const value = create(valuePath, context)
628
637
  const {
629
638
  value: displayValue,
@@ -643,12 +652,12 @@ export abstract class FormModel<
643
652
  // TODO this isn't correct, should reload from value
644
653
  this.fieldOverrides = {}
645
654
  this.errorOverrides = {}
646
- this.value = mobxCopy(this.type, value)
655
+ this.observableValue = mobxCopy(this.type, value)
647
656
  })
648
657
  }
649
658
 
650
659
  isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean {
651
- const values = flattenValuesOfType(this.type, this.value, this.listIndicesToKeys)
660
+ const values = flattenValuesOfType(this.type, this.observableValue, this.listIndicesToKeys)
652
661
  const keys = new Set(Object.keys(values))
653
662
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
654
663
  return keys.has(valuePath as string)
@@ -704,25 +713,23 @@ export abstract class FormModel<
704
713
  return displayedValue !== originalDisplayedValue
705
714
  }
706
715
 
716
+ @action
707
717
  validateField<K extends keyof ValuePathsToAdapters>(
708
718
  valuePath: K,
709
719
  validation: Validation = Validation.Always,
710
720
  ): boolean {
711
- runInAction(() => {
712
- this.validation[valuePath] = validation
713
- delete this.errorOverrides[valuePath]
714
- })
721
+ this.validation[valuePath] = validation
722
+ delete this.errorOverrides[valuePath]
715
723
  return this.fields[valuePath].error == null
716
724
  }
717
725
 
726
+ @action
718
727
  validateAll(validation: Validation = Validation.Always): boolean {
719
728
  const accessors = toArray(this.accessors)
720
729
 
721
- runInAction(() => {
722
- accessors.forEach(([valuePath]) => {
723
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
724
- this.validation[valuePath as keyof ValuePathsToAdapters] = validation
725
- })
730
+ accessors.forEach(([valuePath]) => {
731
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
732
+ this.validation[valuePath as keyof ValuePathsToAdapters] = validation
726
733
  })
727
734
  return accessors.every(
728
735
  ([valuePath]): boolean => {
@@ -10,6 +10,7 @@ import {
10
10
  type FormModel,
11
11
  Validation,
12
12
  } from './FormModel'
13
+ import { peek } from './peek'
13
14
 
14
15
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
16
  type ValueOfModel<M extends FormModel<any, any, any, any, any>> = M extends FormModel<infer T, any, any, any, any>
@@ -41,7 +42,8 @@ export function useDefaultMobxFormHooks<
41
42
  path: Path,
42
43
  value: ValueTypeOfField<F[Path]>,
43
44
  ) {
44
- const validation = Math.min(model.getValidation(path), Validation.Changed)
45
+ const activeValidation = peek(() => model.getValidation(path))
46
+ const validation = Math.min(activeValidation, Validation.Changed)
45
47
  model.setFieldValue<Path>(path, value, validation)
46
48
  },
47
49
  [model],
@@ -66,10 +68,18 @@ export function useDefaultMobxFormHooks<
66
68
  // (e.g. changing a discriminator)
67
69
  // TODO debounce?
68
70
  setTimeout(function () {
71
+ const [
72
+ validate,
73
+ activeValidation,
74
+ ] = peek(() => [
75
+ model.isValuePathActive(path) && model.isFieldDirty(path) && model.fields[path].error == null,
76
+ model.getValidation(path),
77
+ ])
69
78
  // only start validation if the user has changed the field and there isn't already an error visible
70
- if (model.isValuePathActive(path) && model.isFieldDirty(path) && model.fields[path].error == null) {
79
+ if (validate) {
80
+ const validation = Math.max(Validation.Changed, activeValidation)
71
81
  // further workaround to make sure we don't downgrade the existing validation
72
- model.validateField(path, Math.max(Validation.Changed, model.getValidation(path)))
82
+ model.validateField(path, validation)
73
83
  }
74
84
  }, 100)
75
85
  },
@@ -78,8 +88,10 @@ export function useDefaultMobxFormHooks<
78
88
 
79
89
  const onFormSubmit = useCallback(
80
90
  function () {
81
- if (model.validateSubmit()) {
82
- onValidFormSubmit?.(model.value)
91
+ const valid = peek(() => model.validateSubmit())
92
+ if (valid && onValidFormSubmit) {
93
+ const value = peek(() => model.value)
94
+ onValidFormSubmit(value)
83
95
  }
84
96
  },
85
97
  [
@@ -0,0 +1,17 @@
1
+ import { when } from 'mobx'
2
+
3
+ /**
4
+ * Used for when you want to look at the value of an observable without observing it (or triggering
5
+ * the mobx runtime linter)
6
+ */
7
+ export function peek<T>(operation: () => T): T {
8
+ let result: T
9
+ // when will make mobx think we are observing the value
10
+ void when(() => {
11
+ // trick mobx runtime linting
12
+ result = operation()
13
+ return true
14
+ })
15
+ // biome-ignore lint/style/noNonNullAssertion: the result is always there
16
+ return result!
17
+ }
package/dist/index.cjs CHANGED
@@ -110,6 +110,7 @@ __export(index_exports, {
110
110
  mergeAdaptersWithValidators: () => mergeAdaptersWithValidators,
111
111
  mergeFieldAdaptersWithTwoWayConverter: () => mergeFieldAdaptersWithTwoWayConverter,
112
112
  mergeValidators: () => mergeValidators,
113
+ peek: () => peek,
113
114
  prototypingFieldValueFactory: () => prototypingFieldValueFactory,
114
115
  subFormFieldAdapters: () => subFormFieldAdapters,
115
116
  trimmingStringAdapter: () => trimmingStringAdapter,
@@ -363,8 +364,8 @@ var Validation = /* @__PURE__ */ ((Validation2) => {
363
364
  Validation2[Validation2["Always"] = 2] = "Always";
364
365
  return Validation2;
365
366
  })(Validation || {});
366
- var _valueChanged_dec, _dirty_dec, _accessors_dec, _knownFields_dec, _fields_dec, _validation_dec, _errorOverrides_dec, _fieldOverrides_dec, _value_dec, _init, _value, _fieldOverrides, _errorOverrides, _validation;
367
- _value_dec = [import_mobx.observable.ref], _fieldOverrides_dec = [import_mobx.observable.shallow], _errorOverrides_dec = [import_mobx.observable.shallow], _validation_dec = [import_mobx.observable.shallow], _fields_dec = [import_mobx.computed], _knownFields_dec = [import_mobx.computed], _accessors_dec = [import_mobx.computed], _dirty_dec = [import_mobx.computed], _valueChanged_dec = [import_mobx.computed];
367
+ var _validateAll_dec, _validateField_dec, _setFieldValue_dec, _valueChanged_dec, _dirty_dec, _accessors_dec, _knownFields_dec, _fields_dec, _value_dec, _validation_dec, _errorOverrides_dec, _fieldOverrides_dec, _observableValue_dec, _init, _observableValue, _fieldOverrides, _errorOverrides, _validation;
368
+ _observableValue_dec = [import_mobx.observable.ref], _fieldOverrides_dec = [import_mobx.observable.shallow], _errorOverrides_dec = [import_mobx.observable.shallow], _validation_dec = [import_mobx.observable.shallow], _value_dec = [import_mobx.computed], _fields_dec = [import_mobx.computed], _knownFields_dec = [import_mobx.computed], _accessors_dec = [import_mobx.computed], _dirty_dec = [import_mobx.computed], _valueChanged_dec = [import_mobx.computed], _setFieldValue_dec = [import_mobx.action], _validateField_dec = [import_mobx.action], _validateAll_dec = [import_mobx.action];
368
369
  var FormModel = class {
369
370
  constructor(type, originalValue, adapters, mode) {
370
371
  this.type = type;
@@ -372,7 +373,7 @@ var FormModel = class {
372
373
  this.adapters = adapters;
373
374
  this.mode = mode;
374
375
  __runInitializers(_init, 5, this);
375
- __privateAdd(this, _value, __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
376
+ __privateAdd(this, _observableValue, __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
376
377
  __privateAdd(this, _fieldOverrides, __runInitializers(_init, 12, this)), __runInitializers(_init, 15, this);
377
378
  __privateAdd(this, _errorOverrides, __runInitializers(_init, 16, this, {})), __runInitializers(_init, 19, this);
378
379
  __privateAdd(this, _validation, __runInitializers(_init, 20, this, {})), __runInitializers(_init, 23, this);
@@ -383,11 +384,11 @@ var FormModel = class {
383
384
  // maintains the value paths of lists when the original order is destroyed by deletes or reordering
384
385
  __publicField(this, "listIndicesToKeys", {});
385
386
  this.originalValues = (0, import_define.flattenValuesOfType)(type, originalValue, this.listIndicesToKeys);
386
- this.value = (0, import_define.mobxCopy)(type, originalValue);
387
+ this.observableValue = (0, import_define.mobxCopy)(type, originalValue);
387
388
  this.flattenedTypeDefs = (0, import_define.flattenTypesOfType)(type);
388
389
  const conversions = (0, import_define.flattenValueTo)(
389
390
  type,
390
- this.value,
391
+ originalValue,
391
392
  () => {
392
393
  },
393
394
  (_t, fieldValue, _setter, typePath, valuePath) => {
@@ -421,6 +422,9 @@ var FormModel = class {
421
422
  throw new import_base2.UnreachableError(this.mode);
422
423
  }
423
424
  }
425
+ get value() {
426
+ return (0, import_define.copy)(this.type, this.observableValue);
427
+ }
424
428
  get fields() {
425
429
  return new Proxy(
426
430
  this.knownFields,
@@ -440,7 +444,7 @@ var FormModel = class {
440
444
  get knownFields() {
441
445
  return (0, import_define.flattenValueTo)(
442
446
  this.type,
443
- this.value,
447
+ this.observableValue,
444
448
  () => {
445
449
  },
446
450
  // TODO swap these to valuePath, typePath in flatten
@@ -482,7 +486,7 @@ var FormModel = class {
482
486
  const fieldOverride = this.fieldOverrides[valuePath];
483
487
  const accessor = this.getAccessorForValuePath(valuePath);
484
488
  const fieldTypeDef = this.flattenedTypeDefs[typePath];
485
- const context = this.toContext(this.value, valuePath);
489
+ const context = this.toContext(this.observableValue, valuePath);
486
490
  const defaultValue = create(valuePath, context);
487
491
  const {
488
492
  value,
@@ -574,9 +578,9 @@ var FormModel = class {
574
578
  get accessors() {
575
579
  return (0, import_define.flattenAccessorsOfType)(
576
580
  this.type,
577
- this.value,
581
+ this.observableValue,
578
582
  (value) => {
579
- this.value = (0, import_define.mobxCopy)(this.type, value);
583
+ this.observableValue = (0, import_define.mobxCopy)(this.type, value);
580
584
  },
581
585
  this.listIndicesToKeys
582
586
  );
@@ -598,7 +602,7 @@ var FormModel = class {
598
602
  });
599
603
  }
600
604
  get valueChanged() {
601
- return !(0, import_define.equals)(this.type, this.value, this.originalValue);
605
+ return !(0, import_define.equals)(this.type, this.observableValue, this.originalValue);
602
606
  }
603
607
  typePath(valuePath) {
604
608
  return (0, import_define.valuePathToTypePath)(this.type, valuePath, true);
@@ -623,7 +627,7 @@ var FormModel = class {
623
627
  elementTypePath,
624
628
  // TODO what can we use for the value path here?
625
629
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
626
- this.toContext(this.value, valuePath)
630
+ this.toContext(this.observableValue, valuePath)
627
631
  );
628
632
  const originalList = accessor.value;
629
633
  const newList = [
@@ -678,7 +682,7 @@ var FormModel = class {
678
682
  internalSetFieldValue(valuePath, value, validation) {
679
683
  const { revert } = this.getAdapterForValuePath(valuePath);
680
684
  (0, import_base2.assertExists)(revert, "setting value not supported {}", valuePath);
681
- const conversion = revert(value, valuePath, this.toContext(this.value, valuePath));
685
+ const conversion = revert(value, valuePath, this.toContext(this.observableValue, valuePath));
682
686
  const accessor = this.getAccessorForValuePath(valuePath);
683
687
  return (0, import_mobx.runInAction)(() => {
684
688
  this.fieldOverrides[valuePath] = [value];
@@ -733,7 +737,7 @@ var FormModel = class {
733
737
  convert,
734
738
  create
735
739
  } = adapter2;
736
- const context = this.toContext(this.value, valuePath);
740
+ const context = this.toContext(this.observableValue, valuePath);
737
741
  const value = create(valuePath, context);
738
742
  const {
739
743
  value: displayValue
@@ -750,11 +754,11 @@ var FormModel = class {
750
754
  this.validation = {};
751
755
  this.fieldOverrides = {};
752
756
  this.errorOverrides = {};
753
- this.value = (0, import_define.mobxCopy)(this.type, value);
757
+ this.observableValue = (0, import_define.mobxCopy)(this.type, value);
754
758
  });
755
759
  }
756
760
  isValuePathActive(valuePath) {
757
- const values = (0, import_define.flattenValuesOfType)(this.type, this.value, this.listIndicesToKeys);
761
+ const values = (0, import_define.flattenValuesOfType)(this.type, this.observableValue, this.listIndicesToKeys);
758
762
  const keys = new Set(Object.keys(values));
759
763
  return keys.has(valuePath);
760
764
  }
@@ -797,18 +801,14 @@ var FormModel = class {
797
801
  return displayedValue !== originalDisplayedValue;
798
802
  }
799
803
  validateField(valuePath, validation = 2 /* Always */) {
800
- (0, import_mobx.runInAction)(() => {
801
- this.validation[valuePath] = validation;
802
- delete this.errorOverrides[valuePath];
803
- });
804
+ this.validation[valuePath] = validation;
805
+ delete this.errorOverrides[valuePath];
804
806
  return this.fields[valuePath].error == null;
805
807
  }
806
808
  validateAll(validation = 2 /* Always */) {
807
809
  const accessors = (0, import_base2.toArray)(this.accessors);
808
- (0, import_mobx.runInAction)(() => {
809
- accessors.forEach(([valuePath]) => {
810
- this.validation[valuePath] = validation;
811
- });
810
+ accessors.forEach(([valuePath]) => {
811
+ this.validation[valuePath] = validation;
812
812
  });
813
813
  return accessors.every(
814
814
  ([valuePath]) => {
@@ -822,30 +822,48 @@ var FormModel = class {
822
822
  }
823
823
  };
824
824
  _init = __decoratorStart(null);
825
- _value = new WeakMap();
825
+ _observableValue = new WeakMap();
826
826
  _fieldOverrides = new WeakMap();
827
827
  _errorOverrides = new WeakMap();
828
828
  _validation = new WeakMap();
829
- __decorateElement(_init, 4, "value", _value_dec, FormModel, _value);
829
+ __decorateElement(_init, 4, "observableValue", _observableValue_dec, FormModel, _observableValue);
830
830
  __decorateElement(_init, 4, "fieldOverrides", _fieldOverrides_dec, FormModel, _fieldOverrides);
831
831
  __decorateElement(_init, 4, "errorOverrides", _errorOverrides_dec, FormModel, _errorOverrides);
832
832
  __decorateElement(_init, 4, "validation", _validation_dec, FormModel, _validation);
833
+ __decorateElement(_init, 2, "value", _value_dec, FormModel);
833
834
  __decorateElement(_init, 2, "fields", _fields_dec, FormModel);
834
835
  __decorateElement(_init, 2, "knownFields", _knownFields_dec, FormModel);
835
836
  __decorateElement(_init, 2, "accessors", _accessors_dec, FormModel);
836
837
  __decorateElement(_init, 2, "dirty", _dirty_dec, FormModel);
837
838
  __decorateElement(_init, 2, "valueChanged", _valueChanged_dec, FormModel);
839
+ __decorateElement(_init, 1, "setFieldValue", _setFieldValue_dec, FormModel);
840
+ __decorateElement(_init, 1, "validateField", _validateField_dec, FormModel);
841
+ __decorateElement(_init, 1, "validateAll", _validateAll_dec, FormModel);
838
842
  __decoratorMetadata(_init, FormModel);
839
843
 
840
844
  // core/mobx/hooks.tsx
841
845
  var import_react = require("react");
846
+
847
+ // core/mobx/peek.ts
848
+ var import_mobx2 = require("mobx");
849
+ function peek(operation) {
850
+ let result;
851
+ void (0, import_mobx2.when)(() => {
852
+ result = operation();
853
+ return true;
854
+ });
855
+ return result;
856
+ }
857
+
858
+ // core/mobx/hooks.tsx
842
859
  function useDefaultMobxFormHooks(model, {
843
860
  onValidFieldSubmit,
844
861
  onValidFormSubmit
845
862
  } = {}) {
846
863
  const onFieldValueChange = (0, import_react.useCallback)(
847
864
  function(path, value) {
848
- const validation = Math.min(model.getValidation(path), 1 /* Changed */);
865
+ const activeValidation = peek(() => model.getValidation(path));
866
+ const validation = Math.min(activeValidation, 1 /* Changed */);
849
867
  model.setFieldValue(path, value, validation);
850
868
  },
851
869
  [model]
@@ -865,8 +883,16 @@ function useDefaultMobxFormHooks(model, {
865
883
  const onFieldBlur = (0, import_react.useCallback)(
866
884
  function(path) {
867
885
  setTimeout(function() {
868
- if (model.isValuePathActive(path) && model.isFieldDirty(path) && model.fields[path].error == null) {
869
- model.validateField(path, Math.max(1 /* Changed */, model.getValidation(path)));
886
+ const [
887
+ validate4,
888
+ activeValidation
889
+ ] = peek(() => [
890
+ model.isValuePathActive(path) && model.isFieldDirty(path) && model.fields[path].error == null,
891
+ model.getValidation(path)
892
+ ]);
893
+ if (validate4) {
894
+ const validation = Math.max(1 /* Changed */, activeValidation);
895
+ model.validateField(path, validation);
870
896
  }
871
897
  }, 100);
872
898
  },
@@ -874,8 +900,10 @@ function useDefaultMobxFormHooks(model, {
874
900
  );
875
901
  const onFormSubmit = (0, import_react.useCallback)(
876
902
  function() {
877
- if (model.validateSubmit()) {
878
- onValidFormSubmit == null ? void 0 : onValidFormSubmit(model.value);
903
+ const valid = peek(() => model.validateSubmit());
904
+ if (valid && onValidFormSubmit) {
905
+ const value = peek(() => model.value);
906
+ onValidFormSubmit(value);
879
907
  }
880
908
  },
881
909
  [
@@ -1191,7 +1219,7 @@ function DefaultErrorRenderer({
1191
1219
  // mantine/hooks.tsx
1192
1220
  var import_core = require("@mantine/core");
1193
1221
  var import_base7 = require("@strictly/base");
1194
- var import_mobx2 = require("mobx");
1222
+ var import_mobx3 = require("mobx");
1195
1223
  var import_react6 = require("react");
1196
1224
 
1197
1225
  // util/Partial.tsx
@@ -1577,7 +1605,7 @@ function DefaultList({
1577
1605
  indexKeys[index]
1578
1606
  ];
1579
1607
  }).filter(function([
1580
- _value2,
1608
+ _value,
1581
1609
  _index,
1582
1610
  key
1583
1611
  ]) {
@@ -1798,7 +1826,7 @@ function useMantineFormFields({
1798
1826
  []
1799
1827
  );
1800
1828
  (0, import_react6.useEffect)(function() {
1801
- (0, import_mobx2.runInAction)(function() {
1829
+ (0, import_mobx3.runInAction)(function() {
1802
1830
  form.fields = fields;
1803
1831
  });
1804
1832
  }, [
@@ -1832,7 +1860,7 @@ function useMantineFormFields({
1832
1860
  return form;
1833
1861
  }
1834
1862
  var _fields_dec2, _init2, _fields;
1835
- _fields_dec2 = [import_mobx2.observable.ref];
1863
+ _fields_dec2 = [import_mobx3.observable.ref];
1836
1864
  var MantineFormImpl = class {
1837
1865
  constructor(fields) {
1838
1866
  __publicField(this, "textInputCache", new import_base7.Cache(
@@ -2017,6 +2045,7 @@ function mergeValidators(validators1, validators2) {
2017
2045
  mergeAdaptersWithValidators,
2018
2046
  mergeFieldAdaptersWithTwoWayConverter,
2019
2047
  mergeValidators,
2048
+ peek,
2020
2049
  prototypingFieldValueFactory,
2021
2050
  subFormFieldAdapters,
2022
2051
  trimmingStringAdapter,
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Maybe, ElementOfArray, StringConcatOf, StringKeyOf as StringKeyOf$1, ExhaustiveArrayOfUnion, FriendlyExhaustiveArrayOfUnion } from '@strictly/base';
2
- import { Type, ValueOfType, ReadonlyTypeOfType, FlattenedTypesOfType, ListTypeDef, FlattenedValuesOfType, MobxValueOfType, Accessor, Validator, UnionTypeDef, ValueTypesOfDiscriminatedUnion, LiteralTypeDef } from '@strictly/define';
2
+ import { Type, ValueOfType, ReadonlyTypeOfType, FlattenedTypesOfType, ListTypeDef, FlattenedValuesOfType, Accessor, Validator, UnionTypeDef, ValueTypesOfDiscriminatedUnion, LiteralTypeDef } from '@strictly/define';
3
3
  import { ValueOf, SimplifyDeep, ReadonlyDeep, UnionToIntersection, StringKeyOf, Simplify } from 'type-fest';
4
4
  import { ComponentType, RefAttributes, ComponentProps, ForwardRefExoticComponent, PropsWithoutRef, Ref, DependencyList } from 'react';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
@@ -136,7 +136,7 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
136
136
  private readonly originalValue;
137
137
  protected readonly adapters: TypePathsToAdapters;
138
138
  protected readonly mode: FormMode;
139
- accessor value: MobxValueOfType<T>;
139
+ private accessor observableValue;
140
140
  accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>;
141
141
  accessor errorOverrides: FlattenedErrorOverrides<ValuePathsToAdapters>;
142
142
  accessor validation: FlattenedValidation<ValuePathsToAdapters>;
@@ -146,6 +146,7 @@ declare abstract class FormModel<T extends Type, ValueToTypePaths extends Readon
146
146
  constructor(type: T, originalValue: ValueOfType<ReadonlyTypeOfType<T>>, adapters: TypePathsToAdapters, mode: FormMode);
147
147
  protected abstract toContext(value: ValueOfType<ReadonlyTypeOfType<T>>, valuePath: keyof ValuePathsToAdapters): ContextType;
148
148
  get forceMutableFields(): boolean;
149
+ get value(): ValueOfType<ReadonlyTypeOfType<T>>;
149
150
  get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>;
150
151
  private get knownFields();
151
152
  private maybeSynthesizeFieldByValuePath;
@@ -210,6 +211,12 @@ type MergedOfFieldAdaptersWithValidators<FieldAdapters extends Readonly<Record<K
210
211
  type MergedOfFieldAdapterWithValidator<A extends FieldAdapter, V extends Validator | undefined> = undefined extends V ? A : A extends FieldAdapter<infer From, infer To, infer E1, infer P1, infer C1> ? V extends Validator<From, infer E2, infer P2, infer C2> ? FieldAdapter<From, To, E1 | E2, P1 | P2, C1 & C2> : never : never;
211
212
  declare function mergeAdaptersWithValidators<FieldAdapters extends Readonly<Record<Key, FieldAdapter>>, Validators extends Readonly<Record<string, Validator>>, Key extends keyof Validators = keyof Validators>(adapters: FieldAdapters, validators: Validators): MergedOfFieldAdaptersWithValidators<FieldAdapters, Validators, Key>;
212
213
 
214
+ /**
215
+ * Used for when you want to look at the value of an observable without observing it (or triggering
216
+ * the mobx runtime linter)
217
+ */
218
+ declare function peek<T>(operation: () => T): T;
219
+
213
220
  type SubFormFieldAdapter<F extends FieldAdapter, ValuePath extends string> = FieldAdapter<FromOfFieldAdapter<F>, ToOfFieldAdapter<F>, ErrorOfFieldAdapter<F>, ValuePathOfFieldAdapter<F> extends StringConcatOf<'$', infer ValuePathSuffix> ? `${ValuePath}${ValuePathSuffix}` : string, ContextOfFieldAdapter<F>>;
214
221
  type SubFormFieldAdapters<SubAdapters extends Record<string, FieldAdapter>, TypePath extends string, ValuePath extends string> = {
215
222
  [K in keyof SubAdapters as K extends StringConcatOf<'$', infer TypePathSuffix> ? `${TypePath}${TypePathSuffix}` : never]: SubFormFieldAdapter<SubAdapters[K], ValuePath>;
@@ -459,4 +466,4 @@ declare function mergeValidators<Validators1 extends Partial<Readonly<Record<Key
459
466
 
460
467
  declare function Empty(): null;
461
468
 
462
- 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 FormMode, FormModel, type FormProps, type FromOfFieldAdapter, IntegerToStringConverter, type MergedOfFieldAdaptersWithTwoWayConverter, type MergedOfFieldAdaptersWithValidators, type MergedOfValidator, type MergedOfValidators, NullableToBooleanConverter, type PartialComponent, type RefOfProps, SelectDiscriminatedUnionConverter, SelectLiteralConverter, SelectStringConverter, type ToOfFieldAdapter, TrimmingStringConverter, type TwoWayFieldConverter, type TwoWayFieldConverterWithValueFactory, type UnreliableFieldConversion, UnreliableFieldConversionType, type UnreliableFieldConverter, type UnsafePartialComponent, Validation, type ValuePathOfFieldAdapter, type ValuePathsToAdaptersOf, adapter, adapterFromPrototype, adapterFromTwoWayConverter, createPartialComponent, createPartialObserverComponent, createSimplePartialComponent, createUnsafePartialObserverComponent, identityAdapter, listAdapter, mergeAdaptersWithValidators, mergeFieldAdaptersWithTwoWayConverter, mergeValidators, prototypingFieldValueFactory, subFormFieldAdapters, trimmingStringAdapter, useDefaultMobxFormHooks, useMantineFormFields, usePartialComponent, usePartialObserverComponent, validatingConverter };
469
+ 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 FormMode, FormModel, type FormProps, type FromOfFieldAdapter, IntegerToStringConverter, type MergedOfFieldAdaptersWithTwoWayConverter, type MergedOfFieldAdaptersWithValidators, type MergedOfValidator, type MergedOfValidators, NullableToBooleanConverter, type PartialComponent, type RefOfProps, SelectDiscriminatedUnionConverter, SelectLiteralConverter, SelectStringConverter, type ToOfFieldAdapter, TrimmingStringConverter, type TwoWayFieldConverter, type TwoWayFieldConverterWithValueFactory, type UnreliableFieldConversion, UnreliableFieldConversionType, type UnreliableFieldConverter, type UnsafePartialComponent, Validation, type ValuePathOfFieldAdapter, type ValuePathsToAdaptersOf, adapter, adapterFromPrototype, adapterFromTwoWayConverter, createPartialComponent, createPartialObserverComponent, createSimplePartialComponent, createUnsafePartialObserverComponent, identityAdapter, listAdapter, mergeAdaptersWithValidators, mergeFieldAdaptersWithTwoWayConverter, mergeValidators, peek, prototypingFieldValueFactory, subFormFieldAdapters, trimmingStringAdapter, useDefaultMobxFormHooks, useMantineFormFields, usePartialComponent, usePartialObserverComponent, validatingConverter };