@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,7 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import '@mantine/core/styles.css';
3
3
  import { MantineProvider } from '@mantine/core';
4
+ import { configure } from 'mobx';
4
5
  import { StrictMode } from 'react';
6
+ // turn on all useful mobx warnings in storybook to try to catch bad behavior
7
+ configure({
8
+ enforceActions: 'observed',
9
+ observableRequiresReaction: true,
10
+ reactionRequiresObservable: true,
11
+ });
5
12
  const preview = {
6
13
  parameters: {
7
14
  controls: {
@@ -1,5 +1,5 @@
1
1
  import { type ElementOfArray, type Maybe } from '@strictly/base';
2
- import { type Accessor, type FlattenedValuesOfType, type MobxValueOfType, type ReadonlyTypeOfType, type Type, type ValueOfType } from '@strictly/define';
2
+ import { type Accessor, type FlattenedValuesOfType, type ReadonlyTypeOfType, type Type, type ValueOfType } from '@strictly/define';
3
3
  import { type ReadonlyDeep, type SimplifyDeep, type StringKeyOf, type UnionToIntersection, type ValueOf } from 'type-fest';
4
4
  import { type Field } from 'types/Field';
5
5
  import { type ContextOfFieldAdapter, type ErrorOfFieldAdapter, type FieldAdapter, type ToOfFieldAdapter } from './FieldAdapter';
@@ -37,7 +37,7 @@ export declare abstract class FormModel<T extends Type, ValueToTypePaths extends
37
37
  private readonly originalValue;
38
38
  protected readonly adapters: TypePathsToAdapters;
39
39
  protected readonly mode: FormMode;
40
- accessor value: MobxValueOfType<T>;
40
+ private accessor observableValue;
41
41
  accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>;
42
42
  accessor errorOverrides: FlattenedErrorOverrides<ValuePathsToAdapters>;
43
43
  accessor validation: FlattenedValidation<ValuePathsToAdapters>;
@@ -47,6 +47,7 @@ export declare abstract class FormModel<T extends Type, ValueToTypePaths extends
47
47
  constructor(type: T, originalValue: ValueOfType<ReadonlyTypeOfType<T>>, adapters: TypePathsToAdapters, mode: FormMode);
48
48
  protected abstract toContext(value: ValueOfType<ReadonlyTypeOfType<T>>, valuePath: keyof ValuePathsToAdapters): ContextType;
49
49
  get forceMutableFields(): boolean;
50
+ get value(): ValueOfType<ReadonlyTypeOfType<T>>;
50
51
  get fields(): SimplifyDeep<FlattenedConvertedFieldsOf<ValuePathsToAdapters>>;
51
52
  private get knownFields();
52
53
  private maybeSynthesizeFieldByValuePath;
@@ -44,8 +44,8 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
44
44
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
45
45
  };
46
46
  import { assertExists, assertExistsAndReturn, checkValidNumber, map, toArray, UnreachableError, } from '@strictly/base';
47
- import { equals, flattenAccessorsOfType, flattenTypesOfType, flattenValuesOfType, flattenValueTo, jsonPathPop, mobxCopy, valuePathToTypePath, } from '@strictly/define';
48
- import { computed, observable, runInAction, } from 'mobx';
47
+ import { copy, equals, flattenAccessorsOfType, flattenTypesOfType, flattenValuesOfType, flattenValueTo, jsonPathPop, mobxCopy, valuePathToTypePath, } from '@strictly/define';
48
+ import { action, computed, observable, runInAction, } from 'mobx';
49
49
  import { UnreliableFieldConversionType, } from 'types/FieldConverters';
50
50
  export var Validation;
51
51
  (function (Validation) {
@@ -54,12 +54,12 @@ export var Validation;
54
54
  Validation[Validation["Always"] = 2] = "Always";
55
55
  })(Validation || (Validation = {}));
56
56
  let FormModel = (() => {
57
- var _a, _FormModel_value_accessor_storage, _FormModel_fieldOverrides_accessor_storage, _FormModel_errorOverrides_accessor_storage, _FormModel_validation_accessor_storage;
57
+ var _a, _FormModel_observableValue_accessor_storage, _FormModel_fieldOverrides_accessor_storage, _FormModel_errorOverrides_accessor_storage, _FormModel_validation_accessor_storage;
58
58
  var _b, _c, _d, _e;
59
59
  let _instanceExtraInitializers = [];
60
- let _value_decorators;
61
- let _value_initializers = [];
62
- let _value_extraInitializers = [];
60
+ let _observableValue_decorators;
61
+ let _observableValue_initializers = [];
62
+ let _observableValue_extraInitializers = [];
63
63
  let _fieldOverrides_decorators;
64
64
  let _fieldOverrides_initializers = [];
65
65
  let _fieldOverrides_extraInitializers = [];
@@ -69,14 +69,18 @@ let FormModel = (() => {
69
69
  let _validation_decorators;
70
70
  let _validation_initializers = [];
71
71
  let _validation_extraInitializers = [];
72
+ let _get_value_decorators;
72
73
  let _get_fields_decorators;
73
74
  let _get_knownFields_decorators;
74
75
  let _get_accessors_decorators;
75
76
  let _get_dirty_decorators;
76
77
  let _get_valueChanged_decorators;
78
+ let _setFieldValue_decorators;
79
+ let _validateField_decorators;
80
+ let _validateAll_decorators;
77
81
  return _a = class FormModel {
78
- get value() { return __classPrivateFieldGet(this, _FormModel_value_accessor_storage, "f"); }
79
- set value(value) { __classPrivateFieldSet(this, _FormModel_value_accessor_storage, value, "f"); }
82
+ get observableValue() { return __classPrivateFieldGet(this, _FormModel_observableValue_accessor_storage, "f"); }
83
+ set observableValue(value) { __classPrivateFieldSet(this, _FormModel_observableValue_accessor_storage, value, "f"); }
80
84
  get fieldOverrides() { return __classPrivateFieldGet(this, _FormModel_fieldOverrides_accessor_storage, "f"); }
81
85
  set fieldOverrides(value) { __classPrivateFieldSet(this, _FormModel_fieldOverrides_accessor_storage, value, "f"); }
82
86
  get errorOverrides() { return __classPrivateFieldGet(this, _FormModel_errorOverrides_accessor_storage, "f"); }
@@ -108,8 +112,8 @@ let FormModel = (() => {
108
112
  writable: true,
109
113
  value: mode
110
114
  });
111
- _FormModel_value_accessor_storage.set(this, __runInitializers(this, _value_initializers, void 0));
112
- _FormModel_fieldOverrides_accessor_storage.set(this, (__runInitializers(this, _value_extraInitializers), __runInitializers(this, _fieldOverrides_initializers, void 0)));
115
+ _FormModel_observableValue_accessor_storage.set(this, __runInitializers(this, _observableValue_initializers, void 0));
116
+ _FormModel_fieldOverrides_accessor_storage.set(this, (__runInitializers(this, _observableValue_extraInitializers), __runInitializers(this, _fieldOverrides_initializers, void 0)));
113
117
  _FormModel_errorOverrides_accessor_storage.set(this, (__runInitializers(this, _fieldOverrides_extraInitializers), __runInitializers(this, _errorOverrides_initializers, {})));
114
118
  _FormModel_validation_accessor_storage.set(this, (__runInitializers(this, _errorOverrides_extraInitializers), __runInitializers(this, _validation_initializers, {})));
115
119
  Object.defineProperty(this, "flattenedTypeDefs", {
@@ -134,11 +138,11 @@ let FormModel = (() => {
134
138
  value: {}
135
139
  });
136
140
  this.originalValues = flattenValuesOfType(type, originalValue, this.listIndicesToKeys);
137
- this.value = mobxCopy(type, originalValue);
141
+ this.observableValue = mobxCopy(type, originalValue);
138
142
  this.flattenedTypeDefs = flattenTypesOfType(type);
139
143
  // pre-populate field overrides for consistent behavior when default information is overwritten
140
144
  // then returned to
141
- const conversions = flattenValueTo(type, this.value, () => { }, (_t, fieldValue, _setter, typePath, valuePath) => {
145
+ const conversions = flattenValueTo(type, originalValue, () => { }, (_t, fieldValue, _setter, typePath, valuePath) => {
142
146
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
143
147
  const contextValue = this.toContext(originalValue, valuePath);
144
148
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -169,6 +173,10 @@ let FormModel = (() => {
169
173
  throw new UnreachableError(this.mode);
170
174
  }
171
175
  }
176
+ get value() {
177
+ // copy and strip out the mobx so this computed will fire every time anything changes
178
+ return copy(this.type, this.observableValue);
179
+ }
172
180
  get fields() {
173
181
  return new Proxy(this.knownFields, {
174
182
  get: (target, prop) => {
@@ -185,7 +193,7 @@ let FormModel = (() => {
185
193
  });
186
194
  }
187
195
  get knownFields() {
188
- return flattenValueTo(this.type, this.value, () => { },
196
+ return flattenValueTo(this.type, this.observableValue, () => { },
189
197
  // TODO swap these to valuePath, typePath in flatten
190
198
  (_t, _v, _setter, typePath, valuePath) => {
191
199
  return this.synthesizeFieldByPaths(
@@ -222,7 +230,7 @@ let FormModel = (() => {
222
230
  const accessor = this.getAccessorForValuePath(valuePath);
223
231
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
224
232
  const fieldTypeDef = this.flattenedTypeDefs[typePath];
225
- const context = this.toContext(this.value, valuePath);
233
+ const context = this.toContext(this.observableValue, valuePath);
226
234
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
227
235
  const defaultValue = create(valuePath, context);
228
236
  const { value, required, readonly, } = convert(accessor != null
@@ -306,8 +314,8 @@ let FormModel = (() => {
306
314
  return this.accessors[valuePath];
307
315
  }
308
316
  get accessors() {
309
- return flattenAccessorsOfType(this.type, this.value, (value) => {
310
- this.value = mobxCopy(this.type, value);
317
+ return flattenAccessorsOfType(this.type, this.observableValue, (value) => {
318
+ this.observableValue = mobxCopy(this.type, value);
311
319
  }, this.listIndicesToKeys);
312
320
  }
313
321
  maybeGetAdapterForValuePath(valuePath) {
@@ -327,7 +335,7 @@ let FormModel = (() => {
327
335
  }
328
336
  get valueChanged() {
329
337
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
330
- return !equals(this.type, this.value, this.originalValue);
338
+ return !equals(this.type, this.observableValue, this.originalValue);
331
339
  }
332
340
  typePath(valuePath) {
333
341
  return valuePathToTypePath(this.type, valuePath, true);
@@ -354,7 +362,7 @@ let FormModel = (() => {
354
362
  elementTypePath,
355
363
  // TODO what can we use for the value path here?
356
364
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
357
- this.toContext(this.value, valuePath));
365
+ this.toContext(this.observableValue, valuePath));
358
366
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
359
367
  const originalList = accessor.value;
360
368
  const newList = [
@@ -400,7 +408,7 @@ let FormModel = (() => {
400
408
  const { revert } = this.getAdapterForValuePath(valuePath);
401
409
  assertExists(revert, 'setting value not supported {}', valuePath);
402
410
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
403
- const conversion = revert(value, valuePath, this.toContext(this.value, valuePath));
411
+ const conversion = revert(value, valuePath, this.toContext(this.observableValue, valuePath));
404
412
  const accessor = this.getAccessorForValuePath(valuePath);
405
413
  return runInAction(() => {
406
414
  this.fieldOverrides[valuePath] = [value];
@@ -455,7 +463,7 @@ let FormModel = (() => {
455
463
  }
456
464
  const { convert, create, } = adapter;
457
465
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
458
- const context = this.toContext(this.value, valuePath);
466
+ const context = this.toContext(this.observableValue, valuePath);
459
467
  const value = create(valuePath, context);
460
468
  const { value: displayValue, } = convert(value, valuePath, context);
461
469
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -472,11 +480,11 @@ let FormModel = (() => {
472
480
  // TODO this isn't correct, should reload from value
473
481
  this.fieldOverrides = {};
474
482
  this.errorOverrides = {};
475
- this.value = mobxCopy(this.type, value);
483
+ this.observableValue = mobxCopy(this.type, value);
476
484
  });
477
485
  }
478
486
  isValuePathActive(valuePath) {
479
- const values = flattenValuesOfType(this.type, this.value, this.listIndicesToKeys);
487
+ const values = flattenValuesOfType(this.type, this.observableValue, this.listIndicesToKeys);
480
488
  const keys = new Set(Object.keys(values));
481
489
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
482
490
  return keys.has(valuePath);
@@ -516,19 +524,15 @@ let FormModel = (() => {
516
524
  return displayedValue !== originalDisplayedValue;
517
525
  }
518
526
  validateField(valuePath, validation = Validation.Always) {
519
- runInAction(() => {
520
- this.validation[valuePath] = validation;
521
- delete this.errorOverrides[valuePath];
522
- });
527
+ this.validation[valuePath] = validation;
528
+ delete this.errorOverrides[valuePath];
523
529
  return this.fields[valuePath].error == null;
524
530
  }
525
531
  validateAll(validation = Validation.Always) {
526
532
  const accessors = toArray(this.accessors);
527
- runInAction(() => {
528
- accessors.forEach(([valuePath]) => {
529
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
530
- this.validation[valuePath] = validation;
531
- });
533
+ accessors.forEach(([valuePath]) => {
534
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
535
+ this.validation[valuePath] = validation;
532
536
  });
533
537
  return accessors.every(([valuePath]) => {
534
538
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -540,30 +544,38 @@ let FormModel = (() => {
540
544
  return this.validateAll();
541
545
  }
542
546
  },
543
- _FormModel_value_accessor_storage = new WeakMap(),
547
+ _FormModel_observableValue_accessor_storage = new WeakMap(),
544
548
  _FormModel_fieldOverrides_accessor_storage = new WeakMap(),
545
549
  _FormModel_errorOverrides_accessor_storage = new WeakMap(),
546
550
  _FormModel_validation_accessor_storage = new WeakMap(),
547
551
  (() => {
548
552
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
549
- _value_decorators = [(_b = observable).ref.bind(_b)];
553
+ _observableValue_decorators = [(_b = observable).ref.bind(_b)];
550
554
  _fieldOverrides_decorators = [(_c = observable).shallow.bind(_c)];
551
555
  _errorOverrides_decorators = [(_d = observable).shallow.bind(_d)];
552
556
  _validation_decorators = [(_e = observable).shallow.bind(_e)];
557
+ _get_value_decorators = [computed];
553
558
  _get_fields_decorators = [computed];
554
559
  _get_knownFields_decorators = [computed];
555
560
  _get_accessors_decorators = [computed];
556
561
  _get_dirty_decorators = [computed];
557
562
  _get_valueChanged_decorators = [computed];
558
- __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);
563
+ _setFieldValue_decorators = [action];
564
+ _validateField_decorators = [action];
565
+ _validateAll_decorators = [action];
566
+ __esDecorate(_a, null, _observableValue_decorators, { kind: "accessor", name: "observableValue", static: false, private: false, access: { has: obj => "observableValue" in obj, get: obj => obj.observableValue, set: (obj, value) => { obj.observableValue = value; } }, metadata: _metadata }, _observableValue_initializers, _observableValue_extraInitializers);
559
567
  __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);
560
568
  __esDecorate(_a, null, _errorOverrides_decorators, { kind: "accessor", name: "errorOverrides", static: false, private: false, access: { has: obj => "errorOverrides" in obj, get: obj => obj.errorOverrides, set: (obj, value) => { obj.errorOverrides = value; } }, metadata: _metadata }, _errorOverrides_initializers, _errorOverrides_extraInitializers);
561
569
  __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);
570
+ __esDecorate(_a, null, _get_value_decorators, { kind: "getter", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value }, metadata: _metadata }, null, _instanceExtraInitializers);
562
571
  __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);
563
572
  __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);
564
573
  __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);
565
574
  __esDecorate(_a, null, _get_dirty_decorators, { kind: "getter", name: "dirty", static: false, private: false, access: { has: obj => "dirty" in obj, get: obj => obj.dirty }, metadata: _metadata }, null, _instanceExtraInitializers);
566
575
  __esDecorate(_a, null, _get_valueChanged_decorators, { kind: "getter", name: "valueChanged", static: false, private: false, access: { has: obj => "valueChanged" in obj, get: obj => obj.valueChanged }, metadata: _metadata }, null, _instanceExtraInitializers);
576
+ __esDecorate(_a, null, _setFieldValue_decorators, { kind: "method", name: "setFieldValue", static: false, private: false, access: { has: obj => "setFieldValue" in obj, get: obj => obj.setFieldValue }, metadata: _metadata }, null, _instanceExtraInitializers);
577
+ __esDecorate(_a, null, _validateField_decorators, { kind: "method", name: "validateField", static: false, private: false, access: { has: obj => "validateField" in obj, get: obj => obj.validateField }, metadata: _metadata }, null, _instanceExtraInitializers);
578
+ __esDecorate(_a, null, _validateAll_decorators, { kind: "method", name: "validateAll", static: false, private: false, access: { has: obj => "validateAll" in obj, get: obj => obj.validateAll }, metadata: _metadata }, null, _instanceExtraInitializers);
567
579
  if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
568
580
  })(),
569
581
  _a;
@@ -1,8 +1,10 @@
1
1
  import { useCallback, } from 'react';
2
2
  import { Validation, } from './FormModel';
3
+ import { peek } from './peek';
3
4
  export function useDefaultMobxFormHooks(model, { onValidFieldSubmit, onValidFormSubmit, } = {}) {
4
5
  const onFieldValueChange = useCallback(function (path, value) {
5
- const validation = Math.min(model.getValidation(path), Validation.Changed);
6
+ const activeValidation = peek(() => model.getValidation(path));
7
+ const validation = Math.min(activeValidation, Validation.Changed);
6
8
  model.setFieldValue(path, value, validation);
7
9
  }, [model]);
8
10
  const onFieldSubmit = useCallback(function (valuePath) {
@@ -19,16 +21,23 @@ export function useDefaultMobxFormHooks(model, { onValidFieldSubmit, onValidForm
19
21
  // (e.g. changing a discriminator)
20
22
  // TODO debounce?
21
23
  setTimeout(function () {
24
+ const [validate, activeValidation,] = peek(() => [
25
+ model.isValuePathActive(path) && model.isFieldDirty(path) && model.fields[path].error == null,
26
+ model.getValidation(path),
27
+ ]);
22
28
  // only start validation if the user has changed the field and there isn't already an error visible
23
- if (model.isValuePathActive(path) && model.isFieldDirty(path) && model.fields[path].error == null) {
29
+ if (validate) {
30
+ const validation = Math.max(Validation.Changed, activeValidation);
24
31
  // further workaround to make sure we don't downgrade the existing validation
25
- model.validateField(path, Math.max(Validation.Changed, model.getValidation(path)));
32
+ model.validateField(path, validation);
26
33
  }
27
34
  }, 100);
28
35
  }, [model]);
29
36
  const onFormSubmit = useCallback(function () {
30
- if (model.validateSubmit()) {
31
- onValidFormSubmit === null || onValidFormSubmit === void 0 ? void 0 : onValidFormSubmit(model.value);
37
+ const valid = peek(() => model.validateSubmit());
38
+ if (valid && onValidFormSubmit) {
39
+ const value = peek(() => model.value);
40
+ onValidFormSubmit(value);
32
41
  }
33
42
  }, [
34
43
  model,
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Used for when you want to look at the value of an observable without observing it (or triggering
3
+ * the mobx runtime linter)
4
+ */
5
+ export declare function peek<T>(operation: () => T): T;
@@ -0,0 +1,16 @@
1
+ import { when } from 'mobx';
2
+ /**
3
+ * Used for when you want to look at the value of an observable without observing it (or triggering
4
+ * the mobx runtime linter)
5
+ */
6
+ export function peek(operation) {
7
+ let result;
8
+ // when will make mobx think we are observing the value
9
+ void when(() => {
10
+ // trick mobx runtime linting
11
+ result = operation();
12
+ return true;
13
+ });
14
+ // biome-ignore lint/style/noNonNullAssertion: the result is always there
15
+ return result;
16
+ }
package/.out/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export * from './core/mobx/FormModel';
7
7
  export * from './core/mobx/hooks';
8
8
  export * from './core/mobx/mergeFieldAdaptersWithTwoWayConverter';
9
9
  export * from './core/mobx/mergeFieldAdaptersWithValidators';
10
+ export * from './core/mobx/peek';
10
11
  export * from './core/mobx/subFormFieldAdapters';
11
12
  export * from './core/props';
12
13
  export * from './field_converters/IntegerToStringConverter';
package/.out/index.js CHANGED
@@ -7,6 +7,7 @@ export * from './core/mobx/FormModel';
7
7
  export * from './core/mobx/hooks';
8
8
  export * from './core/mobx/mergeFieldAdaptersWithTwoWayConverter';
9
9
  export * from './core/mobx/mergeFieldAdaptersWithValidators';
10
+ export * from './core/mobx/peek';
10
11
  export * from './core/mobx/subFormFieldAdapters';
11
12
  export * from './core/props';
12
13
  export * from './field_converters/IntegerToStringConverter';