@strictly/react-form 0.0.18 → 0.0.19
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/core/mobx/form_model.d.ts +12 -9
- package/.out/core/mobx/form_model.js +103 -137
- package/.out/core/mobx/hooks.js +3 -4
- package/.out/core/mobx/merge_field_adapters_with_two_way_converter.d.ts +1 -1
- package/.out/core/mobx/merge_field_adapters_with_validators.js +5 -1
- package/.out/core/mobx/specs/form_model.tests.js +28 -23
- package/.out/mantine/create_fields_view.d.ts +2 -1
- package/.out/mantine/hooks.d.ts +6 -5
- package/.out/mantine/specs/checkbox_hooks.stories.d.ts +5 -2
- package/.out/mantine/specs/checkbox_hooks.stories.js +3 -2
- package/.out/mantine/specs/text_input_hooks.stories.d.ts +3 -2
- package/.out/mantine/specs/text_input_hooks.stories.js +3 -2
- package/.out/mantine/types.d.ts +3 -3
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.out/util/partial.d.ts +5 -2
- package/.out/util/specs/partial.tests.d.ts +1 -0
- package/.out/util/specs/partial.tests.js +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/form_model.ts +95 -157
- package/core/mobx/hooks.tsx +6 -5
- package/core/mobx/merge_field_adapters_with_two_way_converter.ts +2 -1
- package/core/mobx/merge_field_adapters_with_validators.ts +1 -1
- package/core/mobx/specs/form_model.tests.ts +39 -27
- package/dist/index.cjs +93 -139
- package/dist/index.d.cts +28 -21
- package/dist/index.d.ts +28 -21
- package/dist/index.js +92 -139
- package/mantine/create_fields_view.tsx +8 -4
- package/mantine/hooks.tsx +23 -15
- package/mantine/specs/checkbox_hooks.stories.tsx +7 -1
- package/mantine/specs/text_input_hooks.stories.tsx +8 -1
- package/mantine/types.ts +12 -4
- package/package.json +1 -1
- package/util/partial.tsx +8 -1
- package/util/specs/partial.tests.tsx +21 -0
package/.out/util/partial.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { type FriendlyExhaustiveArrayOfUnion } from '@strictly/base';
|
|
2
|
-
import { type ComponentProps, type ComponentType, type DependencyList, type ForwardRefExoticComponent, type PropsWithoutRef } from 'react';
|
|
2
|
+
import { type ComponentProps, type ComponentType, type DependencyList, type ForwardRefExoticComponent, type PropsWithoutRef, type Ref, type RefAttributes } from 'react';
|
|
3
|
+
export type RefOfProps<P, Fallback = unknown> = P extends RefAttributes<infer R> ? R : Fallback;
|
|
3
4
|
export type PartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps = {}> = Exclude<keyof CurriedProps, keyof ComponentProps<Component>> extends never ? UnsafePartialComponent<Component, CurriedProps, AdditionalProps> : keyof CurriedProps extends (string | number) ? `unmatched prop: ${Exclude<keyof CurriedProps, keyof ComponentProps<Component>>}` : Exclude<keyof CurriedProps, keyof ComponentProps<Component>>;
|
|
4
|
-
export type UnsafePartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps = {}
|
|
5
|
+
export type UnsafePartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps = {}, R = RefOfProps<ComponentProps<Component>>> = ForwardRefExoticComponent<PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps> & {
|
|
6
|
+
ref?: Ref<R>;
|
|
7
|
+
}>;
|
|
5
8
|
export declare function createSimplePartialComponent<Component extends ComponentType<any>, CurriedProps extends Partial<ComponentProps<Component>>>(Component: Component, curriedProps: CurriedProps): PartialComponent<Component, CurriedProps>;
|
|
6
9
|
export declare function createPartialComponent<Component extends ComponentType<any>, CurriedProps>(Component: Component, curriedPropsSource: () => CurriedProps): PartialComponent<Component, CurriedProps, {}>;
|
|
7
10
|
export declare function createPartialComponent<Component extends ComponentType<any>, CurriedProps, AdditionalProps, AllAdditionalPropKeys extends readonly (keyof AdditionalProps)[]>(Component: Component, curriedPropsSource: (additionalProps: AdditionalProps) => CurriedProps, additionalPropKeys: FriendlyExhaustiveArrayOfUnion<keyof AdditionalProps, AllAdditionalPropKeys>): PartialComponent<Component, CurriedProps, AdditionalProps>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
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
|
-
[
|
|
11
|
-
[
|
|
12
|
-
[
|
|
13
|
-
[
|
|
10
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m60.71 KB[39m
|
|
11
|
+
[32mCJS[39m ⚡️ Build success in 113ms
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m56.70 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 113ms
|
|
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 9852ms
|
|
16
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m38.50 KB[39m
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m38.50 KB[39m
|
|
18
|
+
Done in 10.92s.
|
package/core/mobx/form_model.ts
CHANGED
|
@@ -88,10 +88,15 @@ type FlattenedFieldOverrides<
|
|
|
88
88
|
>
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
export enum Validation {
|
|
92
|
+
Changed = 1,
|
|
93
|
+
Always = 2,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
type FlattenedValidation<
|
|
92
97
|
ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>,
|
|
93
98
|
> = {
|
|
94
|
-
-readonly [K in keyof ValuePathsToAdapters]?:
|
|
99
|
+
-readonly [K in keyof ValuePathsToAdapters]?: Validation
|
|
95
100
|
}
|
|
96
101
|
|
|
97
102
|
export type ValuePathsToAdaptersOf<
|
|
@@ -138,16 +143,21 @@ export abstract class FormModel<
|
|
|
138
143
|
@observable.shallow
|
|
139
144
|
accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>
|
|
140
145
|
@observable.shallow
|
|
141
|
-
accessor
|
|
146
|
+
accessor validation: FlattenedValidation<ValuePathsToAdapters> = {}
|
|
142
147
|
|
|
143
148
|
private readonly flattenedTypeDefs: Readonly<Record<string, Type>>
|
|
144
149
|
|
|
150
|
+
// cannot be type safe
|
|
151
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
152
|
+
private readonly originalValues: Record<string, any>
|
|
153
|
+
|
|
145
154
|
constructor(
|
|
146
155
|
readonly type: T,
|
|
147
|
-
|
|
156
|
+
originalValue: ValueOfType<ReadonlyTypeOfType<T>>,
|
|
148
157
|
protected readonly adapters: TypePathsToAdapters,
|
|
149
158
|
protected readonly mode: FormMode,
|
|
150
159
|
) {
|
|
160
|
+
this.originalValues = flattenValuesOfType<ReadonlyTypeOfType<T>>(type, originalValue)
|
|
151
161
|
this.value = mobxCopy(type, originalValue)
|
|
152
162
|
this.flattenedTypeDefs = flattenTypesOfType(type)
|
|
153
163
|
// pre-populate field overrides for consistent behavior when default information is overwritten
|
|
@@ -271,6 +281,7 @@ export abstract class FormModel<
|
|
|
271
281
|
const {
|
|
272
282
|
convert,
|
|
273
283
|
create,
|
|
284
|
+
revert,
|
|
274
285
|
} = adapter
|
|
275
286
|
|
|
276
287
|
const fieldOverride = this.fieldOverrides[valuePath]
|
|
@@ -278,6 +289,9 @@ export abstract class FormModel<
|
|
|
278
289
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
279
290
|
const fieldTypeDef = this.flattenedTypeDefs[typePath as string]
|
|
280
291
|
const context = this.toContext(this.value, valuePath)
|
|
292
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
293
|
+
const defaultValue = create(valuePath as string, context)
|
|
294
|
+
|
|
281
295
|
const {
|
|
282
296
|
value,
|
|
283
297
|
required,
|
|
@@ -288,17 +302,50 @@ export abstract class FormModel<
|
|
|
288
302
|
: fieldTypeDef != null
|
|
289
303
|
? mobxCopy(
|
|
290
304
|
fieldTypeDef,
|
|
291
|
-
|
|
292
|
-
create(valuePath as string, context),
|
|
305
|
+
defaultValue,
|
|
293
306
|
)
|
|
294
307
|
// fake values can't be copied
|
|
295
|
-
|
|
296
|
-
: create(valuePath as string, context),
|
|
308
|
+
: defaultValue,
|
|
297
309
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
298
310
|
valuePath as string,
|
|
299
311
|
context,
|
|
300
312
|
)
|
|
301
|
-
const error = this.errors[valuePath]
|
|
313
|
+
// const error = this.errors[valuePath]
|
|
314
|
+
let error: unknown = undefined
|
|
315
|
+
const displayedValue = fieldOverride != null ? fieldOverride[0] : value
|
|
316
|
+
const validation = this.validation[valuePath]
|
|
317
|
+
switch (validation) {
|
|
318
|
+
case undefined:
|
|
319
|
+
// skip validation
|
|
320
|
+
break
|
|
321
|
+
case Validation.Changed:
|
|
322
|
+
if (revert != null) {
|
|
323
|
+
const originalValue = valuePath in this.originalValues
|
|
324
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
325
|
+
? this.originalValues[valuePath as string]
|
|
326
|
+
: defaultValue
|
|
327
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
328
|
+
const { value: originalDisplayedValue } = convert(originalValue, valuePath as string, context)
|
|
329
|
+
// TODO better comparisons, displayed values can still be complex
|
|
330
|
+
if (displayedValue !== originalDisplayedValue) {
|
|
331
|
+
const revertResult = revert(displayedValue, valuePath, context)
|
|
332
|
+
if (revertResult?.type === UnreliableFieldConversionType.Failure) {
|
|
333
|
+
;({ error } = revertResult)
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
break
|
|
338
|
+
case Validation.Always:
|
|
339
|
+
{
|
|
340
|
+
const revertResult = revert?.(displayedValue, valuePath, context)
|
|
341
|
+
if (revertResult?.type === UnreliableFieldConversionType.Failure) {
|
|
342
|
+
;({ error } = revertResult)
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
break
|
|
346
|
+
default:
|
|
347
|
+
throw new UnreachableError(validation)
|
|
348
|
+
}
|
|
302
349
|
return {
|
|
303
350
|
value: fieldOverride != null ? fieldOverride[0] : value,
|
|
304
351
|
error,
|
|
@@ -343,18 +390,12 @@ export abstract class FormModel<
|
|
|
343
390
|
return valuePathToTypePath<ValueToTypePaths, K>(this.type, valuePath, true)
|
|
344
391
|
}
|
|
345
392
|
|
|
346
|
-
setFieldValueAndValidate<K extends keyof ValuePathsToAdapters>(
|
|
347
|
-
valuePath: K,
|
|
348
|
-
value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
|
|
349
|
-
): boolean {
|
|
350
|
-
return this.internalSetFieldValue(valuePath, value, true)
|
|
351
|
-
}
|
|
352
|
-
|
|
353
393
|
setFieldValue<K extends keyof ValuePathsToAdapters>(
|
|
354
394
|
valuePath: K,
|
|
355
395
|
value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
|
|
396
|
+
validation: Validation | undefined | null = this.validation[valuePath],
|
|
356
397
|
): boolean {
|
|
357
|
-
return this.internalSetFieldValue(valuePath, value,
|
|
398
|
+
return this.internalSetFieldValue(valuePath, value, validation)
|
|
358
399
|
}
|
|
359
400
|
|
|
360
401
|
addListItem<K extends keyof FlattenedListTypesOfType<T>>(
|
|
@@ -430,9 +471,9 @@ export abstract class FormModel<
|
|
|
430
471
|
const fieldOverride = this.fieldOverrides[fromJsonPath]
|
|
431
472
|
delete this.fieldOverrides[fromJsonPath]
|
|
432
473
|
this.fieldOverrides[toJsonPath] = fieldOverride
|
|
433
|
-
const
|
|
434
|
-
delete this.
|
|
435
|
-
this.
|
|
474
|
+
const validation = this.validation[fromJsonPath]
|
|
475
|
+
delete this.validation[fromJsonPath]
|
|
476
|
+
this.validation[toJsonPath] = validation
|
|
436
477
|
})
|
|
437
478
|
accessor.set(newList)
|
|
438
479
|
// delete any value overrides so the new list isn't shadowed
|
|
@@ -507,9 +548,9 @@ export abstract class FormModel<
|
|
|
507
548
|
const fieldOverride = this.fieldOverrides[fromJsonPath]
|
|
508
549
|
delete this.fieldOverrides[fromJsonPath]
|
|
509
550
|
this.fieldOverrides[toJsonPath] = fieldOverride
|
|
510
|
-
const
|
|
511
|
-
delete this.
|
|
512
|
-
this.
|
|
551
|
+
const validation = this.validation[fromJsonPath]
|
|
552
|
+
delete this.validation[fromJsonPath]
|
|
553
|
+
this.validation[toJsonPath] = validation
|
|
513
554
|
})
|
|
514
555
|
accessor.set(newList)
|
|
515
556
|
// delete any value overrides so the new list isn't shadowed
|
|
@@ -522,7 +563,7 @@ export abstract class FormModel<
|
|
|
522
563
|
private internalSetFieldValue<K extends keyof ValuePathsToAdapters>(
|
|
523
564
|
valuePath: K,
|
|
524
565
|
value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
|
|
525
|
-
|
|
566
|
+
validation: Validation | undefined | null,
|
|
526
567
|
): boolean {
|
|
527
568
|
const { revert } = this.getAdapterForValuePath(valuePath)
|
|
528
569
|
|
|
@@ -533,17 +574,18 @@ export abstract class FormModel<
|
|
|
533
574
|
const accessor = this.getAccessorForValuePath(valuePath)
|
|
534
575
|
return runInAction(() => {
|
|
535
576
|
this.fieldOverrides[valuePath] = [value]
|
|
577
|
+
if (validation != null) {
|
|
578
|
+
this.validation[valuePath] = validation
|
|
579
|
+
} else {
|
|
580
|
+
delete this.validation[valuePath]
|
|
581
|
+
}
|
|
536
582
|
switch (conversion.type) {
|
|
537
583
|
case UnreliableFieldConversionType.Failure:
|
|
538
|
-
if (displayValidation) {
|
|
539
|
-
this.errors[valuePath] = conversion.error
|
|
540
|
-
}
|
|
541
584
|
if (conversion.value != null && accessor != null) {
|
|
542
585
|
accessor.set(conversion.value[0])
|
|
543
586
|
}
|
|
544
587
|
return false
|
|
545
588
|
case UnreliableFieldConversionType.Success:
|
|
546
|
-
delete this.errors[valuePath]
|
|
547
589
|
accessor?.set(conversion.value)
|
|
548
590
|
return true
|
|
549
591
|
default:
|
|
@@ -556,12 +598,12 @@ export abstract class FormModel<
|
|
|
556
598
|
const fieldOverride = this.fieldOverrides[valuePath]
|
|
557
599
|
if (fieldOverride != null) {
|
|
558
600
|
runInAction(() => {
|
|
559
|
-
delete this.
|
|
601
|
+
delete this.validation[valuePath]
|
|
560
602
|
})
|
|
561
603
|
}
|
|
562
604
|
}
|
|
563
605
|
|
|
564
|
-
clearFieldValue<K extends StringKeyOf<
|
|
606
|
+
clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K) {
|
|
565
607
|
const typePath = this.typePath(valuePath)
|
|
566
608
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
567
609
|
const adapter = this.adapters[typePath as keyof TypePathsToAdapters]
|
|
@@ -582,12 +624,13 @@ export abstract class FormModel<
|
|
|
582
624
|
const key = valuePath as unknown as keyof ValuePathsToAdapters
|
|
583
625
|
runInAction(() => {
|
|
584
626
|
this.fieldOverrides[key] = [displayValue]
|
|
627
|
+
delete this.validation[key]
|
|
585
628
|
})
|
|
586
629
|
}
|
|
587
630
|
|
|
588
631
|
clearAll(value: ValueOfType<T>): void {
|
|
589
632
|
runInAction(() => {
|
|
590
|
-
this.
|
|
633
|
+
this.validation = {}
|
|
591
634
|
// TODO this isn't correct, should reload from value
|
|
592
635
|
this.fieldOverrides = {}
|
|
593
636
|
this.value = mobxCopy(this.type, value)
|
|
@@ -603,137 +646,32 @@ export abstract class FormModel<
|
|
|
603
646
|
|
|
604
647
|
validateField<K extends keyof ValuePathsToAdapters>(
|
|
605
648
|
valuePath: K,
|
|
606
|
-
|
|
649
|
+
validation: Validation = Math.max(
|
|
650
|
+
this.mode === 'create' ? Validation.Always : Validation.Changed,
|
|
651
|
+
this.validation[valuePath] ?? Validation.Changed,
|
|
652
|
+
),
|
|
607
653
|
): boolean {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
revert,
|
|
611
|
-
create,
|
|
612
|
-
} = this.getAdapterForValuePath(valuePath)
|
|
613
|
-
const fieldOverride = this.fieldOverrides[valuePath]
|
|
614
|
-
const accessor = this.getAccessorForValuePath(valuePath)
|
|
615
|
-
const context = this.toContext(this.value, valuePath)
|
|
616
|
-
|
|
617
|
-
const {
|
|
618
|
-
value: storedValue,
|
|
619
|
-
} = convert(
|
|
620
|
-
accessor != null
|
|
621
|
-
? accessor.value
|
|
622
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
623
|
-
: create(valuePath as string, context),
|
|
624
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
625
|
-
valuePath as string,
|
|
626
|
-
context,
|
|
627
|
-
)
|
|
628
|
-
const value = fieldOverride != null
|
|
629
|
-
? fieldOverride[0]
|
|
630
|
-
: storedValue
|
|
631
|
-
const dirty = storedValue !== value
|
|
632
|
-
assertExists(revert, 'changing field directly not supported {}', valuePath)
|
|
633
|
-
if (ignoreDefaultValue) {
|
|
634
|
-
const {
|
|
635
|
-
value: defaultDisplayValue,
|
|
636
|
-
} = convert(create(valuePath, context), valuePath, context)
|
|
637
|
-
if (defaultDisplayValue === value) {
|
|
638
|
-
return true
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
642
|
-
const conversion = revert(value, valuePath as string, context)
|
|
643
|
-
return runInAction(() => {
|
|
644
|
-
switch (conversion.type) {
|
|
645
|
-
case UnreliableFieldConversionType.Failure:
|
|
646
|
-
this.errors[valuePath] = conversion.error
|
|
647
|
-
if (conversion.value != null && accessor != null && dirty) {
|
|
648
|
-
accessor.set(conversion.value[0])
|
|
649
|
-
}
|
|
650
|
-
return false
|
|
651
|
-
case UnreliableFieldConversionType.Success:
|
|
652
|
-
delete this.errors[valuePath]
|
|
653
|
-
if (accessor != null && dirty) {
|
|
654
|
-
accessor.set(conversion.value)
|
|
655
|
-
}
|
|
656
|
-
return true
|
|
657
|
-
default:
|
|
658
|
-
throw new UnreachableError(conversion)
|
|
659
|
-
}
|
|
654
|
+
runInAction(() => {
|
|
655
|
+
this.validation[valuePath] = validation
|
|
660
656
|
})
|
|
657
|
+
return this.fields[valuePath].error == null
|
|
661
658
|
}
|
|
662
659
|
|
|
663
|
-
validateAll(
|
|
664
|
-
|
|
665
|
-
const accessors = toArray(this.accessors).toSorted(function ([a], [b]) {
|
|
666
|
-
return a.length - b.length
|
|
667
|
-
})
|
|
668
|
-
|
|
669
|
-
const flattenedOriginalValues = flattenValuesOfType(this.type, this.originalValue)
|
|
660
|
+
validateAll(validation: Validation = this.mode === 'create' ? Validation.Always : Validation.Changed): boolean {
|
|
661
|
+
const accessors = toArray(this.accessors)
|
|
670
662
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
valuePath,
|
|
677
|
-
accessor,
|
|
678
|
-
],
|
|
679
|
-
): boolean => {
|
|
680
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
681
|
-
const adapterPath = valuePath as keyof ValuePathsToAdapters
|
|
682
|
-
const adapter = this.maybeGetAdapterForValuePath(adapterPath)
|
|
683
|
-
if (adapter == null) {
|
|
684
|
-
// no adapter == there should be nothing specified for this field
|
|
685
|
-
return success
|
|
686
|
-
}
|
|
687
|
-
const {
|
|
688
|
-
convert,
|
|
689
|
-
revert,
|
|
690
|
-
} = adapter
|
|
691
|
-
if (revert == null) {
|
|
692
|
-
// no convert method means this field is immutable
|
|
693
|
-
return success
|
|
694
|
-
}
|
|
695
|
-
const fieldOverride = this.fieldOverrides[adapterPath]
|
|
696
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
697
|
-
const context = this.toContext(this.value, valuePath as keyof ValuePathsToAdapters)
|
|
698
|
-
const {
|
|
699
|
-
value: storedValue,
|
|
700
|
-
} = convert(accessor.value, valuePath, context)
|
|
701
|
-
const value = fieldOverride != null
|
|
702
|
-
? fieldOverride[0]
|
|
703
|
-
: storedValue
|
|
704
|
-
// TODO customizable comparisons
|
|
705
|
-
const dirty = fieldOverride != null && fieldOverride[0] !== storedValue
|
|
706
|
-
const needsValidation = force
|
|
707
|
-
|| !(valuePath in flattenedOriginalValues)
|
|
708
|
-
|| storedValue !== convert(
|
|
709
|
-
flattenedOriginalValues[valuePath],
|
|
710
|
-
valuePath,
|
|
711
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
712
|
-
this.toContext(this.originalValue, valuePath as keyof ValuePathsToAdapters),
|
|
713
|
-
).value
|
|
714
|
-
if (needsValidation) {
|
|
715
|
-
const conversion = revert(value, valuePath, context)
|
|
716
|
-
switch (conversion.type) {
|
|
717
|
-
case UnreliableFieldConversionType.Failure:
|
|
718
|
-
this.errors[adapterPath] = conversion.error
|
|
719
|
-
if (conversion.value != null && dirty) {
|
|
720
|
-
accessor.set(conversion.value[0])
|
|
721
|
-
}
|
|
722
|
-
return false
|
|
723
|
-
case UnreliableFieldConversionType.Success:
|
|
724
|
-
if (dirty) {
|
|
725
|
-
accessor.set(conversion.value)
|
|
726
|
-
}
|
|
727
|
-
delete this.errors[adapterPath]
|
|
728
|
-
return success
|
|
729
|
-
default:
|
|
730
|
-
throw new UnreachableError(conversion)
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
return success
|
|
734
|
-
},
|
|
735
|
-
true,
|
|
736
|
-
)
|
|
663
|
+
runInAction(() => {
|
|
664
|
+
accessors.forEach(([valuePath]) => {
|
|
665
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
666
|
+
this.validation[valuePath as keyof ValuePathsToAdapters] = validation
|
|
667
|
+
})
|
|
737
668
|
})
|
|
669
|
+
return accessors.every(
|
|
670
|
+
([valuePath]): boolean => {
|
|
671
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
672
|
+
const field = this.fields[valuePath as keyof ValuePathsToAdapters]
|
|
673
|
+
return field?.error == null
|
|
674
|
+
},
|
|
675
|
+
)
|
|
738
676
|
}
|
|
739
677
|
}
|
package/core/mobx/hooks.tsx
CHANGED
|
@@ -5,7 +5,9 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
useCallback,
|
|
7
7
|
} from 'react'
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
type FormModel,
|
|
10
|
+
} from './form_model'
|
|
9
11
|
import {
|
|
10
12
|
type FormFieldsOfModel,
|
|
11
13
|
type ToValueOfModelValuePath,
|
|
@@ -42,9 +44,8 @@ export function useDefaultMobxFormHooks<
|
|
|
42
44
|
path: Path,
|
|
43
45
|
value: ToValueOfModelValuePath<M, Path>,
|
|
44
46
|
) {
|
|
45
|
-
//
|
|
46
|
-
model.
|
|
47
|
-
model.setFieldValue<Path>(path, value)
|
|
47
|
+
// clear any validation
|
|
48
|
+
model.setFieldValue<Path>(path, value, null)
|
|
48
49
|
},
|
|
49
50
|
[model],
|
|
50
51
|
)
|
|
@@ -69,7 +70,7 @@ export function useDefaultMobxFormHooks<
|
|
|
69
70
|
// TODO debounce?
|
|
70
71
|
setTimeout(function () {
|
|
71
72
|
if (model.isValuePathActive(path)) {
|
|
72
|
-
model.validateField(path
|
|
73
|
+
model.validateField(path)
|
|
73
74
|
}
|
|
74
75
|
}, 100)
|
|
75
76
|
},
|
|
@@ -40,13 +40,14 @@ export function mergeFieldAdaptersWithTwoWayConverter<
|
|
|
40
40
|
FieldAdapters extends Readonly<Record<string, FieldAdapter>>,
|
|
41
41
|
E,
|
|
42
42
|
Context,
|
|
43
|
+
P extends ValuePathsOfFieldAdapters<FieldAdapters>,
|
|
43
44
|
>(
|
|
44
45
|
fieldAdapters: FieldAdapters,
|
|
45
46
|
converter: TwoWayFieldConverter<
|
|
46
47
|
TosOfFieldAdapters<FieldAdapters>,
|
|
47
48
|
TosOfFieldAdapters<FieldAdapters>,
|
|
48
49
|
E,
|
|
49
|
-
|
|
50
|
+
P,
|
|
50
51
|
Context
|
|
51
52
|
>,
|
|
52
53
|
): MergedOfFieldAdaptersWithTwoWayConverter<FieldAdapters, E, Context> {
|