@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.
- package/.out/.storybook/preview.js +7 -0
- package/.out/core/mobx/FormModel.d.ts +3 -2
- package/.out/core/mobx/FormModel.js +46 -34
- package/.out/core/mobx/hooks.js +14 -5
- package/.out/core/mobx/peek.d.ts +5 -0
- package/.out/core/mobx/peek.js +16 -0
- package/.out/index.d.ts +1 -0
- package/.out/index.js +1 -0
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.storybook/preview.tsx +8 -0
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-check-types.log +1 -1
- package/.turbo/turbo-release$colon$exports.log +1 -1
- package/core/mobx/FormModel.ts +29 -22
- package/core/mobx/hooks.tsx +17 -5
- package/core/mobx/peek.ts +17 -0
- package/dist/index.cjs +63 -34
- package/dist/index.d.cts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +65 -35
- package/index.ts +1 -0
- package/package.json +1 -1
package/.storybook/preview.tsx
CHANGED
|
@@ -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: {
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -7,12 +7,12 @@ $ tsup
|
|
|
7
7
|
[34mCLI[39m Target: es6
|
|
8
8
|
[34mCJS[39m Build start
|
|
9
9
|
[34mESM[39m Build start
|
|
10
|
-
[32mCJS[39m [1mdist/index.cjs [22m[
|
|
11
|
-
[32mCJS[39m ⚡️ Build success in
|
|
12
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
13
|
-
[32mESM[39m ⚡️ Build success in
|
|
10
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m64.18 KB[39m
|
|
11
|
+
[32mCJS[39m ⚡️ Build success in 121ms
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m60.02 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 126ms
|
|
14
14
|
[34mDTS[39m Build start
|
|
15
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[32m38.
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m38.
|
|
18
|
-
Done in
|
|
15
|
+
[32mDTS[39m ⚡️ Build success in 6938ms
|
|
16
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m38.43 KB[39m
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m38.43 KB[39m
|
|
18
|
+
Done in 8.16s.
|
package/core/mobx/FormModel.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
425
|
+
this.observableValue,
|
|
418
426
|
(value: ValueOfType<T>): void => {
|
|
419
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
712
|
-
|
|
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
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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 => {
|
package/core/mobx/hooks.tsx
CHANGED
|
@@ -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
|
|
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 (
|
|
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,
|
|
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
|
-
|
|
82
|
-
|
|
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,
|
|
367
|
-
|
|
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,
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
581
|
+
this.observableValue,
|
|
578
582
|
(value) => {
|
|
579
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
801
|
-
|
|
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
|
-
|
|
809
|
-
|
|
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
|
-
|
|
825
|
+
_observableValue = new WeakMap();
|
|
826
826
|
_fieldOverrides = new WeakMap();
|
|
827
827
|
_errorOverrides = new WeakMap();
|
|
828
828
|
_validation = new WeakMap();
|
|
829
|
-
__decorateElement(_init, 4, "
|
|
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
|
|
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
|
-
|
|
869
|
-
|
|
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
|
-
|
|
878
|
-
|
|
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
|
|
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
|
-
|
|
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,
|
|
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 = [
|
|
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,
|
|
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
|
|
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 };
|