@strictly/react-form 0.0.17 → 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 +13 -10
- 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/core/props.d.ts +0 -2
- 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/core/mobx/form_model.ts +97 -158
- 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/core/props.ts +0 -4
- package/dist/index.cjs +93 -139
- package/dist/index.d.cts +41 -35
- package/dist/index.d.ts +41 -35
- 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
|
-
[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[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 10.
|
|
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
|
@@ -26,7 +26,6 @@ import {
|
|
|
26
26
|
type ValueOfType,
|
|
27
27
|
valuePathToTypePath,
|
|
28
28
|
} from '@strictly/define'
|
|
29
|
-
import { type FormMode } from 'core/props'
|
|
30
29
|
import {
|
|
31
30
|
computed,
|
|
32
31
|
observable,
|
|
@@ -89,10 +88,15 @@ type FlattenedFieldOverrides<
|
|
|
89
88
|
>
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
|
|
91
|
+
export enum Validation {
|
|
92
|
+
Changed = 1,
|
|
93
|
+
Always = 2,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
type FlattenedValidation<
|
|
93
97
|
ValuePathsToAdapters extends Readonly<Record<string, FieldAdapter>>,
|
|
94
98
|
> = {
|
|
95
|
-
-readonly [K in keyof ValuePathsToAdapters]?:
|
|
99
|
+
-readonly [K in keyof ValuePathsToAdapters]?: Validation
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
export type ValuePathsToAdaptersOf<
|
|
@@ -119,6 +123,8 @@ export type ContextOf<TypePathsToAdapters extends Partial<Readonly<Record<string
|
|
|
119
123
|
| {}
|
|
120
124
|
>
|
|
121
125
|
|
|
126
|
+
export type FormMode = 'edit' | 'create'
|
|
127
|
+
|
|
122
128
|
export abstract class FormModel<
|
|
123
129
|
T extends Type,
|
|
124
130
|
ValueToTypePaths extends Readonly<Record<string, string>>,
|
|
@@ -137,16 +143,21 @@ export abstract class FormModel<
|
|
|
137
143
|
@observable.shallow
|
|
138
144
|
accessor fieldOverrides: FlattenedFieldOverrides<ValuePathsToAdapters>
|
|
139
145
|
@observable.shallow
|
|
140
|
-
accessor
|
|
146
|
+
accessor validation: FlattenedValidation<ValuePathsToAdapters> = {}
|
|
141
147
|
|
|
142
148
|
private readonly flattenedTypeDefs: Readonly<Record<string, Type>>
|
|
143
149
|
|
|
150
|
+
// cannot be type safe
|
|
151
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
152
|
+
private readonly originalValues: Record<string, any>
|
|
153
|
+
|
|
144
154
|
constructor(
|
|
145
155
|
readonly type: T,
|
|
146
|
-
|
|
156
|
+
originalValue: ValueOfType<ReadonlyTypeOfType<T>>,
|
|
147
157
|
protected readonly adapters: TypePathsToAdapters,
|
|
148
158
|
protected readonly mode: FormMode,
|
|
149
159
|
) {
|
|
160
|
+
this.originalValues = flattenValuesOfType<ReadonlyTypeOfType<T>>(type, originalValue)
|
|
150
161
|
this.value = mobxCopy(type, originalValue)
|
|
151
162
|
this.flattenedTypeDefs = flattenTypesOfType(type)
|
|
152
163
|
// pre-populate field overrides for consistent behavior when default information is overwritten
|
|
@@ -270,6 +281,7 @@ export abstract class FormModel<
|
|
|
270
281
|
const {
|
|
271
282
|
convert,
|
|
272
283
|
create,
|
|
284
|
+
revert,
|
|
273
285
|
} = adapter
|
|
274
286
|
|
|
275
287
|
const fieldOverride = this.fieldOverrides[valuePath]
|
|
@@ -277,6 +289,9 @@ export abstract class FormModel<
|
|
|
277
289
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
278
290
|
const fieldTypeDef = this.flattenedTypeDefs[typePath as string]
|
|
279
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
|
+
|
|
280
295
|
const {
|
|
281
296
|
value,
|
|
282
297
|
required,
|
|
@@ -287,17 +302,50 @@ export abstract class FormModel<
|
|
|
287
302
|
: fieldTypeDef != null
|
|
288
303
|
? mobxCopy(
|
|
289
304
|
fieldTypeDef,
|
|
290
|
-
|
|
291
|
-
create(valuePath as string, context),
|
|
305
|
+
defaultValue,
|
|
292
306
|
)
|
|
293
307
|
// fake values can't be copied
|
|
294
|
-
|
|
295
|
-
: create(valuePath as string, context),
|
|
308
|
+
: defaultValue,
|
|
296
309
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
297
310
|
valuePath as string,
|
|
298
311
|
context,
|
|
299
312
|
)
|
|
300
|
-
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
|
+
}
|
|
301
349
|
return {
|
|
302
350
|
value: fieldOverride != null ? fieldOverride[0] : value,
|
|
303
351
|
error,
|
|
@@ -342,18 +390,12 @@ export abstract class FormModel<
|
|
|
342
390
|
return valuePathToTypePath<ValueToTypePaths, K>(this.type, valuePath, true)
|
|
343
391
|
}
|
|
344
392
|
|
|
345
|
-
setFieldValueAndValidate<K extends keyof ValuePathsToAdapters>(
|
|
346
|
-
valuePath: K,
|
|
347
|
-
value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
|
|
348
|
-
): boolean {
|
|
349
|
-
return this.internalSetFieldValue(valuePath, value, true)
|
|
350
|
-
}
|
|
351
|
-
|
|
352
393
|
setFieldValue<K extends keyof ValuePathsToAdapters>(
|
|
353
394
|
valuePath: K,
|
|
354
395
|
value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
|
|
396
|
+
validation: Validation | undefined | null = this.validation[valuePath],
|
|
355
397
|
): boolean {
|
|
356
|
-
return this.internalSetFieldValue(valuePath, value,
|
|
398
|
+
return this.internalSetFieldValue(valuePath, value, validation)
|
|
357
399
|
}
|
|
358
400
|
|
|
359
401
|
addListItem<K extends keyof FlattenedListTypesOfType<T>>(
|
|
@@ -429,9 +471,9 @@ export abstract class FormModel<
|
|
|
429
471
|
const fieldOverride = this.fieldOverrides[fromJsonPath]
|
|
430
472
|
delete this.fieldOverrides[fromJsonPath]
|
|
431
473
|
this.fieldOverrides[toJsonPath] = fieldOverride
|
|
432
|
-
const
|
|
433
|
-
delete this.
|
|
434
|
-
this.
|
|
474
|
+
const validation = this.validation[fromJsonPath]
|
|
475
|
+
delete this.validation[fromJsonPath]
|
|
476
|
+
this.validation[toJsonPath] = validation
|
|
435
477
|
})
|
|
436
478
|
accessor.set(newList)
|
|
437
479
|
// delete any value overrides so the new list isn't shadowed
|
|
@@ -506,9 +548,9 @@ export abstract class FormModel<
|
|
|
506
548
|
const fieldOverride = this.fieldOverrides[fromJsonPath]
|
|
507
549
|
delete this.fieldOverrides[fromJsonPath]
|
|
508
550
|
this.fieldOverrides[toJsonPath] = fieldOverride
|
|
509
|
-
const
|
|
510
|
-
delete this.
|
|
511
|
-
this.
|
|
551
|
+
const validation = this.validation[fromJsonPath]
|
|
552
|
+
delete this.validation[fromJsonPath]
|
|
553
|
+
this.validation[toJsonPath] = validation
|
|
512
554
|
})
|
|
513
555
|
accessor.set(newList)
|
|
514
556
|
// delete any value overrides so the new list isn't shadowed
|
|
@@ -521,7 +563,7 @@ export abstract class FormModel<
|
|
|
521
563
|
private internalSetFieldValue<K extends keyof ValuePathsToAdapters>(
|
|
522
564
|
valuePath: K,
|
|
523
565
|
value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
|
|
524
|
-
|
|
566
|
+
validation: Validation | undefined | null,
|
|
525
567
|
): boolean {
|
|
526
568
|
const { revert } = this.getAdapterForValuePath(valuePath)
|
|
527
569
|
|
|
@@ -532,17 +574,18 @@ export abstract class FormModel<
|
|
|
532
574
|
const accessor = this.getAccessorForValuePath(valuePath)
|
|
533
575
|
return runInAction(() => {
|
|
534
576
|
this.fieldOverrides[valuePath] = [value]
|
|
577
|
+
if (validation != null) {
|
|
578
|
+
this.validation[valuePath] = validation
|
|
579
|
+
} else {
|
|
580
|
+
delete this.validation[valuePath]
|
|
581
|
+
}
|
|
535
582
|
switch (conversion.type) {
|
|
536
583
|
case UnreliableFieldConversionType.Failure:
|
|
537
|
-
if (displayValidation) {
|
|
538
|
-
this.errors[valuePath] = conversion.error
|
|
539
|
-
}
|
|
540
584
|
if (conversion.value != null && accessor != null) {
|
|
541
585
|
accessor.set(conversion.value[0])
|
|
542
586
|
}
|
|
543
587
|
return false
|
|
544
588
|
case UnreliableFieldConversionType.Success:
|
|
545
|
-
delete this.errors[valuePath]
|
|
546
589
|
accessor?.set(conversion.value)
|
|
547
590
|
return true
|
|
548
591
|
default:
|
|
@@ -555,12 +598,12 @@ export abstract class FormModel<
|
|
|
555
598
|
const fieldOverride = this.fieldOverrides[valuePath]
|
|
556
599
|
if (fieldOverride != null) {
|
|
557
600
|
runInAction(() => {
|
|
558
|
-
delete this.
|
|
601
|
+
delete this.validation[valuePath]
|
|
559
602
|
})
|
|
560
603
|
}
|
|
561
604
|
}
|
|
562
605
|
|
|
563
|
-
clearFieldValue<K extends StringKeyOf<
|
|
606
|
+
clearFieldValue<K extends StringKeyOf<ValuePathsToAdapters>>(valuePath: K) {
|
|
564
607
|
const typePath = this.typePath(valuePath)
|
|
565
608
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
566
609
|
const adapter = this.adapters[typePath as keyof TypePathsToAdapters]
|
|
@@ -581,12 +624,13 @@ export abstract class FormModel<
|
|
|
581
624
|
const key = valuePath as unknown as keyof ValuePathsToAdapters
|
|
582
625
|
runInAction(() => {
|
|
583
626
|
this.fieldOverrides[key] = [displayValue]
|
|
627
|
+
delete this.validation[key]
|
|
584
628
|
})
|
|
585
629
|
}
|
|
586
630
|
|
|
587
631
|
clearAll(value: ValueOfType<T>): void {
|
|
588
632
|
runInAction(() => {
|
|
589
|
-
this.
|
|
633
|
+
this.validation = {}
|
|
590
634
|
// TODO this isn't correct, should reload from value
|
|
591
635
|
this.fieldOverrides = {}
|
|
592
636
|
this.value = mobxCopy(this.type, value)
|
|
@@ -602,137 +646,32 @@ export abstract class FormModel<
|
|
|
602
646
|
|
|
603
647
|
validateField<K extends keyof ValuePathsToAdapters>(
|
|
604
648
|
valuePath: K,
|
|
605
|
-
|
|
649
|
+
validation: Validation = Math.max(
|
|
650
|
+
this.mode === 'create' ? Validation.Always : Validation.Changed,
|
|
651
|
+
this.validation[valuePath] ?? Validation.Changed,
|
|
652
|
+
),
|
|
606
653
|
): boolean {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
revert,
|
|
610
|
-
create,
|
|
611
|
-
} = this.getAdapterForValuePath(valuePath)
|
|
612
|
-
const fieldOverride = this.fieldOverrides[valuePath]
|
|
613
|
-
const accessor = this.getAccessorForValuePath(valuePath)
|
|
614
|
-
const context = this.toContext(this.value, valuePath)
|
|
615
|
-
|
|
616
|
-
const {
|
|
617
|
-
value: storedValue,
|
|
618
|
-
} = convert(
|
|
619
|
-
accessor != null
|
|
620
|
-
? accessor.value
|
|
621
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
622
|
-
: create(valuePath as string, context),
|
|
623
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
624
|
-
valuePath as string,
|
|
625
|
-
context,
|
|
626
|
-
)
|
|
627
|
-
const value = fieldOverride != null
|
|
628
|
-
? fieldOverride[0]
|
|
629
|
-
: storedValue
|
|
630
|
-
const dirty = storedValue !== value
|
|
631
|
-
assertExists(revert, 'changing field directly not supported {}', valuePath)
|
|
632
|
-
if (ignoreDefaultValue) {
|
|
633
|
-
const {
|
|
634
|
-
value: defaultDisplayValue,
|
|
635
|
-
} = convert(create(valuePath, context), valuePath, context)
|
|
636
|
-
if (defaultDisplayValue === value) {
|
|
637
|
-
return true
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
641
|
-
const conversion = revert(value, valuePath as string, context)
|
|
642
|
-
return runInAction(() => {
|
|
643
|
-
switch (conversion.type) {
|
|
644
|
-
case UnreliableFieldConversionType.Failure:
|
|
645
|
-
this.errors[valuePath] = conversion.error
|
|
646
|
-
if (conversion.value != null && accessor != null && dirty) {
|
|
647
|
-
accessor.set(conversion.value[0])
|
|
648
|
-
}
|
|
649
|
-
return false
|
|
650
|
-
case UnreliableFieldConversionType.Success:
|
|
651
|
-
delete this.errors[valuePath]
|
|
652
|
-
if (accessor != null && dirty) {
|
|
653
|
-
accessor.set(conversion.value)
|
|
654
|
-
}
|
|
655
|
-
return true
|
|
656
|
-
default:
|
|
657
|
-
throw new UnreachableError(conversion)
|
|
658
|
-
}
|
|
654
|
+
runInAction(() => {
|
|
655
|
+
this.validation[valuePath] = validation
|
|
659
656
|
})
|
|
657
|
+
return this.fields[valuePath].error == null
|
|
660
658
|
}
|
|
661
659
|
|
|
662
|
-
validateAll(
|
|
663
|
-
|
|
664
|
-
const accessors = toArray(this.accessors).toSorted(function ([a], [b]) {
|
|
665
|
-
return a.length - b.length
|
|
666
|
-
})
|
|
667
|
-
|
|
668
|
-
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)
|
|
669
662
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
valuePath,
|
|
676
|
-
accessor,
|
|
677
|
-
],
|
|
678
|
-
): boolean => {
|
|
679
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
680
|
-
const adapterPath = valuePath as keyof ValuePathsToAdapters
|
|
681
|
-
const adapter = this.maybeGetAdapterForValuePath(adapterPath)
|
|
682
|
-
if (adapter == null) {
|
|
683
|
-
// no adapter == there should be nothing specified for this field
|
|
684
|
-
return success
|
|
685
|
-
}
|
|
686
|
-
const {
|
|
687
|
-
convert,
|
|
688
|
-
revert,
|
|
689
|
-
} = adapter
|
|
690
|
-
if (revert == null) {
|
|
691
|
-
// no convert method means this field is immutable
|
|
692
|
-
return success
|
|
693
|
-
}
|
|
694
|
-
const fieldOverride = this.fieldOverrides[adapterPath]
|
|
695
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
696
|
-
const context = this.toContext(this.value, valuePath as keyof ValuePathsToAdapters)
|
|
697
|
-
const {
|
|
698
|
-
value: storedValue,
|
|
699
|
-
} = convert(accessor.value, valuePath, context)
|
|
700
|
-
const value = fieldOverride != null
|
|
701
|
-
? fieldOverride[0]
|
|
702
|
-
: storedValue
|
|
703
|
-
// TODO customizable comparisons
|
|
704
|
-
const dirty = fieldOverride != null && fieldOverride[0] !== storedValue
|
|
705
|
-
const needsValidation = force
|
|
706
|
-
|| !(valuePath in flattenedOriginalValues)
|
|
707
|
-
|| storedValue !== convert(
|
|
708
|
-
flattenedOriginalValues[valuePath],
|
|
709
|
-
valuePath,
|
|
710
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
711
|
-
this.toContext(this.originalValue, valuePath as keyof ValuePathsToAdapters),
|
|
712
|
-
).value
|
|
713
|
-
if (needsValidation) {
|
|
714
|
-
const conversion = revert(value, valuePath, context)
|
|
715
|
-
switch (conversion.type) {
|
|
716
|
-
case UnreliableFieldConversionType.Failure:
|
|
717
|
-
this.errors[adapterPath] = conversion.error
|
|
718
|
-
if (conversion.value != null && dirty) {
|
|
719
|
-
accessor.set(conversion.value[0])
|
|
720
|
-
}
|
|
721
|
-
return false
|
|
722
|
-
case UnreliableFieldConversionType.Success:
|
|
723
|
-
if (dirty) {
|
|
724
|
-
accessor.set(conversion.value)
|
|
725
|
-
}
|
|
726
|
-
delete this.errors[adapterPath]
|
|
727
|
-
return success
|
|
728
|
-
default:
|
|
729
|
-
throw new UnreachableError(conversion)
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
return success
|
|
733
|
-
},
|
|
734
|
-
true,
|
|
735
|
-
)
|
|
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
|
+
})
|
|
736
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
|
+
)
|
|
737
676
|
}
|
|
738
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> {
|