@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/dist/index.js
CHANGED
|
@@ -326,19 +326,27 @@ import {
|
|
|
326
326
|
observable,
|
|
327
327
|
runInAction
|
|
328
328
|
} from "mobx";
|
|
329
|
-
var
|
|
330
|
-
|
|
329
|
+
var Validation = /* @__PURE__ */ ((Validation2) => {
|
|
330
|
+
Validation2[Validation2["Changed"] = 1] = "Changed";
|
|
331
|
+
Validation2[Validation2["Always"] = 2] = "Always";
|
|
332
|
+
return Validation2;
|
|
333
|
+
})(Validation || {});
|
|
334
|
+
var _accessors_dec, _knownFields_dec, _fields_dec, _validation_dec, _fieldOverrides_dec, _value_dec, _init, _value, _fieldOverrides, _validation;
|
|
335
|
+
_value_dec = [observable.ref], _fieldOverrides_dec = [observable.shallow], _validation_dec = [observable.shallow], _fields_dec = [computed], _knownFields_dec = [computed], _accessors_dec = [computed];
|
|
331
336
|
var FormModel = class {
|
|
332
337
|
constructor(type, originalValue, adapters, mode) {
|
|
333
338
|
this.type = type;
|
|
334
|
-
this.originalValue = originalValue;
|
|
335
339
|
this.adapters = adapters;
|
|
336
340
|
this.mode = mode;
|
|
337
341
|
__runInitializers(_init, 5, this);
|
|
338
342
|
__privateAdd(this, _value, __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
|
|
339
343
|
__privateAdd(this, _fieldOverrides, __runInitializers(_init, 12, this)), __runInitializers(_init, 15, this);
|
|
340
|
-
__privateAdd(this,
|
|
344
|
+
__privateAdd(this, _validation, __runInitializers(_init, 16, this, {})), __runInitializers(_init, 19, this);
|
|
341
345
|
__publicField(this, "flattenedTypeDefs");
|
|
346
|
+
// cannot be type safe
|
|
347
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
348
|
+
__publicField(this, "originalValues");
|
|
349
|
+
this.originalValues = flattenValuesOfType(type, originalValue);
|
|
342
350
|
this.value = mobxCopy(type, originalValue);
|
|
343
351
|
this.flattenedTypeDefs = flattenTypesOfType(type);
|
|
344
352
|
const conversions = flattenValueTo(
|
|
@@ -430,12 +438,14 @@ var FormModel = class {
|
|
|
430
438
|
}
|
|
431
439
|
const {
|
|
432
440
|
convert,
|
|
433
|
-
create
|
|
441
|
+
create,
|
|
442
|
+
revert
|
|
434
443
|
} = adapter2;
|
|
435
444
|
const fieldOverride = this.fieldOverrides[valuePath];
|
|
436
445
|
const accessor = this.getAccessorForValuePath(valuePath);
|
|
437
446
|
const fieldTypeDef = this.flattenedTypeDefs[typePath];
|
|
438
447
|
const context = this.toContext(this.value, valuePath);
|
|
448
|
+
const defaultValue = create(valuePath, context);
|
|
439
449
|
const {
|
|
440
450
|
value,
|
|
441
451
|
required,
|
|
@@ -443,14 +453,43 @@ var FormModel = class {
|
|
|
443
453
|
} = convert(
|
|
444
454
|
accessor != null ? accessor.value : fieldTypeDef != null ? mobxCopy(
|
|
445
455
|
fieldTypeDef,
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
) : create(valuePath, context),
|
|
456
|
+
defaultValue
|
|
457
|
+
) : defaultValue,
|
|
449
458
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
450
459
|
valuePath,
|
|
451
460
|
context
|
|
452
461
|
);
|
|
453
|
-
|
|
462
|
+
let error = void 0;
|
|
463
|
+
const displayedValue = fieldOverride != null ? fieldOverride[0] : value;
|
|
464
|
+
const validation = this.validation[valuePath];
|
|
465
|
+
switch (validation) {
|
|
466
|
+
case void 0:
|
|
467
|
+
break;
|
|
468
|
+
case 1 /* Changed */:
|
|
469
|
+
if (revert != null) {
|
|
470
|
+
const originalValue = valuePath in this.originalValues ? this.originalValues[valuePath] : defaultValue;
|
|
471
|
+
const { value: originalDisplayedValue } = convert(originalValue, valuePath, context);
|
|
472
|
+
if (displayedValue !== originalDisplayedValue) {
|
|
473
|
+
const revertResult = revert(displayedValue, valuePath, context);
|
|
474
|
+
if ((revertResult == null ? void 0 : revertResult.type) === 1 /* Failure */) {
|
|
475
|
+
;
|
|
476
|
+
({ error } = revertResult);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
case 2 /* Always */:
|
|
482
|
+
{
|
|
483
|
+
const revertResult = revert == null ? void 0 : revert(displayedValue, valuePath, context);
|
|
484
|
+
if ((revertResult == null ? void 0 : revertResult.type) === 1 /* Failure */) {
|
|
485
|
+
;
|
|
486
|
+
({ error } = revertResult);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
break;
|
|
490
|
+
default:
|
|
491
|
+
throw new UnreachableError2(validation);
|
|
492
|
+
}
|
|
454
493
|
return {
|
|
455
494
|
value: fieldOverride != null ? fieldOverride[0] : value,
|
|
456
495
|
error,
|
|
@@ -485,11 +524,8 @@ var FormModel = class {
|
|
|
485
524
|
typePath(valuePath) {
|
|
486
525
|
return valuePathToTypePath(this.type, valuePath, true);
|
|
487
526
|
}
|
|
488
|
-
|
|
489
|
-
return this.internalSetFieldValue(valuePath, value,
|
|
490
|
-
}
|
|
491
|
-
setFieldValue(valuePath, value) {
|
|
492
|
-
return this.internalSetFieldValue(valuePath, value, false);
|
|
527
|
+
setFieldValue(valuePath, value, validation = this.validation[valuePath]) {
|
|
528
|
+
return this.internalSetFieldValue(valuePath, value, validation);
|
|
493
529
|
}
|
|
494
530
|
addListItem(valuePath, elementValue = null, index) {
|
|
495
531
|
const listValuePath = valuePath;
|
|
@@ -548,9 +584,9 @@ var FormModel = class {
|
|
|
548
584
|
const fieldOverride = this.fieldOverrides[fromJsonPath];
|
|
549
585
|
delete this.fieldOverrides[fromJsonPath];
|
|
550
586
|
this.fieldOverrides[toJsonPath] = fieldOverride;
|
|
551
|
-
const
|
|
552
|
-
delete this.
|
|
553
|
-
this.
|
|
587
|
+
const validation = this.validation[fromJsonPath];
|
|
588
|
+
delete this.validation[fromJsonPath];
|
|
589
|
+
this.validation[toJsonPath] = validation;
|
|
554
590
|
});
|
|
555
591
|
accessor.set(newList);
|
|
556
592
|
delete this.fieldOverrides[listValuePath];
|
|
@@ -614,33 +650,34 @@ var FormModel = class {
|
|
|
614
650
|
const fieldOverride = this.fieldOverrides[fromJsonPath];
|
|
615
651
|
delete this.fieldOverrides[fromJsonPath];
|
|
616
652
|
this.fieldOverrides[toJsonPath] = fieldOverride;
|
|
617
|
-
const
|
|
618
|
-
delete this.
|
|
619
|
-
this.
|
|
653
|
+
const validation = this.validation[fromJsonPath];
|
|
654
|
+
delete this.validation[fromJsonPath];
|
|
655
|
+
this.validation[toJsonPath] = validation;
|
|
620
656
|
});
|
|
621
657
|
accessor.set(newList);
|
|
622
658
|
delete this.fieldOverrides[listValuePath];
|
|
623
659
|
});
|
|
624
660
|
});
|
|
625
661
|
}
|
|
626
|
-
internalSetFieldValue(valuePath, value,
|
|
662
|
+
internalSetFieldValue(valuePath, value, validation) {
|
|
627
663
|
const { revert } = this.getAdapterForValuePath(valuePath);
|
|
628
664
|
assertExists(revert, "setting value not supported {}", valuePath);
|
|
629
665
|
const conversion = revert(value, valuePath, this.toContext(this.value, valuePath));
|
|
630
666
|
const accessor = this.getAccessorForValuePath(valuePath);
|
|
631
667
|
return runInAction(() => {
|
|
632
668
|
this.fieldOverrides[valuePath] = [value];
|
|
669
|
+
if (validation != null) {
|
|
670
|
+
this.validation[valuePath] = validation;
|
|
671
|
+
} else {
|
|
672
|
+
delete this.validation[valuePath];
|
|
673
|
+
}
|
|
633
674
|
switch (conversion.type) {
|
|
634
675
|
case 1 /* Failure */:
|
|
635
|
-
if (displayValidation) {
|
|
636
|
-
this.errors[valuePath] = conversion.error;
|
|
637
|
-
}
|
|
638
676
|
if (conversion.value != null && accessor != null) {
|
|
639
677
|
accessor.set(conversion.value[0]);
|
|
640
678
|
}
|
|
641
679
|
return false;
|
|
642
680
|
case 0 /* Success */:
|
|
643
|
-
delete this.errors[valuePath];
|
|
644
681
|
accessor == null ? void 0 : accessor.set(conversion.value);
|
|
645
682
|
return true;
|
|
646
683
|
default:
|
|
@@ -652,7 +689,7 @@ var FormModel = class {
|
|
|
652
689
|
const fieldOverride = this.fieldOverrides[valuePath];
|
|
653
690
|
if (fieldOverride != null) {
|
|
654
691
|
runInAction(() => {
|
|
655
|
-
delete this.
|
|
692
|
+
delete this.validation[valuePath];
|
|
656
693
|
});
|
|
657
694
|
}
|
|
658
695
|
}
|
|
@@ -674,11 +711,12 @@ var FormModel = class {
|
|
|
674
711
|
const key = valuePath;
|
|
675
712
|
runInAction(() => {
|
|
676
713
|
this.fieldOverrides[key] = [displayValue];
|
|
714
|
+
delete this.validation[key];
|
|
677
715
|
});
|
|
678
716
|
}
|
|
679
717
|
clearAll(value) {
|
|
680
718
|
runInAction(() => {
|
|
681
|
-
this.
|
|
719
|
+
this.validation = {};
|
|
682
720
|
this.fieldOverrides = {};
|
|
683
721
|
this.value = mobxCopy(this.type, value);
|
|
684
722
|
});
|
|
@@ -688,123 +726,37 @@ var FormModel = class {
|
|
|
688
726
|
const keys = new Set(Object.keys(values));
|
|
689
727
|
return keys.has(valuePath);
|
|
690
728
|
}
|
|
691
|
-
validateField(valuePath,
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const fieldOverride = this.fieldOverrides[valuePath];
|
|
698
|
-
const accessor = this.getAccessorForValuePath(valuePath);
|
|
699
|
-
const context = this.toContext(this.value, valuePath);
|
|
700
|
-
const {
|
|
701
|
-
value: storedValue
|
|
702
|
-
} = convert(
|
|
703
|
-
accessor != null ? accessor.value : create(valuePath, context),
|
|
704
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
705
|
-
valuePath,
|
|
706
|
-
context
|
|
707
|
-
);
|
|
708
|
-
const value = fieldOverride != null ? fieldOverride[0] : storedValue;
|
|
709
|
-
const dirty = storedValue !== value;
|
|
710
|
-
assertExists(revert, "changing field directly not supported {}", valuePath);
|
|
711
|
-
if (ignoreDefaultValue) {
|
|
712
|
-
const {
|
|
713
|
-
value: defaultDisplayValue
|
|
714
|
-
} = convert(create(valuePath, context), valuePath, context);
|
|
715
|
-
if (defaultDisplayValue === value) {
|
|
716
|
-
return true;
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
const conversion = revert(value, valuePath, context);
|
|
720
|
-
return runInAction(() => {
|
|
721
|
-
switch (conversion.type) {
|
|
722
|
-
case 1 /* Failure */:
|
|
723
|
-
this.errors[valuePath] = conversion.error;
|
|
724
|
-
if (conversion.value != null && accessor != null && dirty) {
|
|
725
|
-
accessor.set(conversion.value[0]);
|
|
726
|
-
}
|
|
727
|
-
return false;
|
|
728
|
-
case 0 /* Success */:
|
|
729
|
-
delete this.errors[valuePath];
|
|
730
|
-
if (accessor != null && dirty) {
|
|
731
|
-
accessor.set(conversion.value);
|
|
732
|
-
}
|
|
733
|
-
return true;
|
|
734
|
-
default:
|
|
735
|
-
throw new UnreachableError2(conversion);
|
|
736
|
-
}
|
|
729
|
+
validateField(valuePath, validation = Math.max(
|
|
730
|
+
this.mode === "create" ? 2 /* Always */ : 1 /* Changed */,
|
|
731
|
+
((_a) => (_a = this.validation[valuePath]) != null ? _a : 1 /* Changed */)()
|
|
732
|
+
)) {
|
|
733
|
+
runInAction(() => {
|
|
734
|
+
this.validation[valuePath] = validation;
|
|
737
735
|
});
|
|
736
|
+
return this.fields[valuePath].error == null;
|
|
738
737
|
}
|
|
739
|
-
validateAll(
|
|
740
|
-
const accessors = toArray(this.accessors)
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
return accessors.reduce(
|
|
746
|
-
(success, [
|
|
747
|
-
valuePath,
|
|
748
|
-
accessor
|
|
749
|
-
]) => {
|
|
750
|
-
const adapterPath = valuePath;
|
|
751
|
-
const adapter2 = this.maybeGetAdapterForValuePath(adapterPath);
|
|
752
|
-
if (adapter2 == null) {
|
|
753
|
-
return success;
|
|
754
|
-
}
|
|
755
|
-
const {
|
|
756
|
-
convert,
|
|
757
|
-
revert
|
|
758
|
-
} = adapter2;
|
|
759
|
-
if (revert == null) {
|
|
760
|
-
return success;
|
|
761
|
-
}
|
|
762
|
-
const fieldOverride = this.fieldOverrides[adapterPath];
|
|
763
|
-
const context = this.toContext(this.value, valuePath);
|
|
764
|
-
const {
|
|
765
|
-
value: storedValue
|
|
766
|
-
} = convert(accessor.value, valuePath, context);
|
|
767
|
-
const value = fieldOverride != null ? fieldOverride[0] : storedValue;
|
|
768
|
-
const dirty = fieldOverride != null && fieldOverride[0] !== storedValue;
|
|
769
|
-
const needsValidation = force || !(valuePath in flattenedOriginalValues) || storedValue !== convert(
|
|
770
|
-
flattenedOriginalValues[valuePath],
|
|
771
|
-
valuePath,
|
|
772
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
773
|
-
this.toContext(this.originalValue, valuePath)
|
|
774
|
-
).value;
|
|
775
|
-
if (needsValidation) {
|
|
776
|
-
const conversion = revert(value, valuePath, context);
|
|
777
|
-
switch (conversion.type) {
|
|
778
|
-
case 1 /* Failure */:
|
|
779
|
-
this.errors[adapterPath] = conversion.error;
|
|
780
|
-
if (conversion.value != null && dirty) {
|
|
781
|
-
accessor.set(conversion.value[0]);
|
|
782
|
-
}
|
|
783
|
-
return false;
|
|
784
|
-
case 0 /* Success */:
|
|
785
|
-
if (dirty) {
|
|
786
|
-
accessor.set(conversion.value);
|
|
787
|
-
}
|
|
788
|
-
delete this.errors[adapterPath];
|
|
789
|
-
return success;
|
|
790
|
-
default:
|
|
791
|
-
throw new UnreachableError2(conversion);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
return success;
|
|
795
|
-
},
|
|
796
|
-
true
|
|
797
|
-
);
|
|
738
|
+
validateAll(validation = this.mode === "create" ? 2 /* Always */ : 1 /* Changed */) {
|
|
739
|
+
const accessors = toArray(this.accessors);
|
|
740
|
+
runInAction(() => {
|
|
741
|
+
accessors.forEach(([valuePath]) => {
|
|
742
|
+
this.validation[valuePath] = validation;
|
|
743
|
+
});
|
|
798
744
|
});
|
|
745
|
+
return accessors.every(
|
|
746
|
+
([valuePath]) => {
|
|
747
|
+
const field = this.fields[valuePath];
|
|
748
|
+
return (field == null ? void 0 : field.error) == null;
|
|
749
|
+
}
|
|
750
|
+
);
|
|
799
751
|
}
|
|
800
752
|
};
|
|
801
753
|
_init = __decoratorStart(null);
|
|
802
754
|
_value = new WeakMap();
|
|
803
755
|
_fieldOverrides = new WeakMap();
|
|
804
|
-
|
|
756
|
+
_validation = new WeakMap();
|
|
805
757
|
__decorateElement(_init, 4, "value", _value_dec, FormModel, _value);
|
|
806
758
|
__decorateElement(_init, 4, "fieldOverrides", _fieldOverrides_dec, FormModel, _fieldOverrides);
|
|
807
|
-
__decorateElement(_init, 4, "
|
|
759
|
+
__decorateElement(_init, 4, "validation", _validation_dec, FormModel, _validation);
|
|
808
760
|
__decorateElement(_init, 2, "fields", _fields_dec, FormModel);
|
|
809
761
|
__decorateElement(_init, 2, "knownFields", _knownFields_dec, FormModel);
|
|
810
762
|
__decorateElement(_init, 2, "accessors", _accessors_dec, FormModel);
|
|
@@ -820,8 +772,7 @@ function useDefaultMobxFormHooks(model, {
|
|
|
820
772
|
} = {}) {
|
|
821
773
|
const onFieldValueChange = useCallback(
|
|
822
774
|
function(path, value) {
|
|
823
|
-
model.
|
|
824
|
-
model.setFieldValue(path, value);
|
|
775
|
+
model.setFieldValue(path, value, null);
|
|
825
776
|
},
|
|
826
777
|
[model]
|
|
827
778
|
);
|
|
@@ -841,7 +792,7 @@ function useDefaultMobxFormHooks(model, {
|
|
|
841
792
|
function(path) {
|
|
842
793
|
setTimeout(function() {
|
|
843
794
|
if (model.isValuePathActive(path)) {
|
|
844
|
-
model.validateField(path
|
|
795
|
+
model.validateField(path);
|
|
845
796
|
}
|
|
846
797
|
}, 100);
|
|
847
798
|
},
|
|
@@ -933,10 +884,11 @@ function mergeAdaptersWithValidators(adapters, validators) {
|
|
|
933
884
|
readonly: readonly1 || readonly2
|
|
934
885
|
};
|
|
935
886
|
}
|
|
936
|
-
acc[key] =
|
|
887
|
+
acc[key] = {
|
|
888
|
+
create: adapter2.create.bind(adapter2),
|
|
937
889
|
convert,
|
|
938
890
|
revert: adapter2.revert && revert
|
|
939
|
-
}
|
|
891
|
+
};
|
|
940
892
|
return acc;
|
|
941
893
|
},
|
|
942
894
|
{}
|
|
@@ -1997,6 +1949,7 @@ export {
|
|
|
1997
1949
|
SelectStringConverter,
|
|
1998
1950
|
TrimmingStringConverter,
|
|
1999
1951
|
UnreliableFieldConversionType,
|
|
1952
|
+
Validation,
|
|
2000
1953
|
adapter,
|
|
2001
1954
|
adapterFromPrototype,
|
|
2002
1955
|
adapterFromTwoWayConverter,
|
|
@@ -15,14 +15,18 @@ import type { SubFormFields } from 'types/sub_form_fields'
|
|
|
15
15
|
import type { ValueTypeOfField } from 'types/value_type_of_field'
|
|
16
16
|
import type { MantineFieldComponent } from './types'
|
|
17
17
|
|
|
18
|
+
export type SubPathsOf<ValuePath extends string, SubFormValuePath extends string> = SubFormValuePath extends
|
|
19
|
+
StringConcatOf<ValuePath, infer Postfix> ? `$${Postfix}`
|
|
20
|
+
: never
|
|
21
|
+
|
|
18
22
|
export type CallbackMapper<ValuePath extends string> = {
|
|
19
23
|
<
|
|
20
24
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
25
|
Cb extends (...args: any[]) => any,
|
|
22
|
-
>(cb: Cb): Parameters<Cb> extends [infer SubFormValuePath extends string, ...(infer Rest)]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
>(cb: Cb): Parameters<Cb> extends [infer SubFormValuePath extends string, ...(infer Rest)] ? (
|
|
27
|
+
valuePath: SubPathsOf<ValuePath, SubFormValuePath>,
|
|
28
|
+
...rest: Rest
|
|
29
|
+
) => ReturnType<Cb>
|
|
26
30
|
: never,
|
|
27
31
|
}
|
|
28
32
|
|
package/mantine/hooks.tsx
CHANGED
|
@@ -40,6 +40,7 @@ import { type ListFieldsOfFields } from 'types/list_fields_of_fields'
|
|
|
40
40
|
import { type StringFieldsOfFields } from 'types/string_fields_of_fields'
|
|
41
41
|
import { type SubFormFields } from 'types/sub_form_fields'
|
|
42
42
|
import { type ValueTypeOfField } from 'types/value_type_of_field'
|
|
43
|
+
import { type RefOfProps } from 'util/partial'
|
|
43
44
|
import {
|
|
44
45
|
createCheckbox,
|
|
45
46
|
type SuppliedCheckboxProps,
|
|
@@ -154,11 +155,11 @@ class MantineFormImpl<
|
|
|
154
155
|
private readonly valueInputCache: Cache<
|
|
155
156
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
157
|
[keyof AllFieldsOfFields<F>, ComponentType<SuppliedValueInputProps<any>>],
|
|
157
|
-
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
159
|
+
MantineFieldComponent<SuppliedValueInputProps<any>>
|
|
158
160
|
> = new Cache(
|
|
159
161
|
createValueInput.bind(this),
|
|
160
162
|
)
|
|
161
|
-
|
|
162
163
|
private readonly checkboxCache: Cache<
|
|
163
164
|
[keyof BooleanFieldsOfFields<F>, ComponentType<SuppliedCheckboxProps>],
|
|
164
165
|
MantineFieldComponent<SuppliedCheckboxProps>
|
|
@@ -223,7 +224,7 @@ class MantineFormImpl<
|
|
|
223
224
|
|
|
224
225
|
textInput<
|
|
225
226
|
K extends keyof StringFieldsOfFields<F>,
|
|
226
|
-
>(valuePath: K): MantineFieldComponent<SuppliedTextInputProps, TextInputProps, ErrorOfField<F[K]
|
|
227
|
+
>(valuePath: K): MantineFieldComponent<SuppliedTextInputProps, TextInputProps, ErrorOfField<F[K]>, HTMLInputElement>
|
|
227
228
|
textInput<
|
|
228
229
|
K extends keyof StringFieldsOfFields<F>,
|
|
229
230
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -231,7 +232,7 @@ class MantineFormImpl<
|
|
|
231
232
|
>(
|
|
232
233
|
valuePath: K,
|
|
233
234
|
TextInput?: ComponentType<P>,
|
|
234
|
-
): MantineFieldComponent<SuppliedTextInputProps, P, ErrorOfField<F[K]>>
|
|
235
|
+
): MantineFieldComponent<SuppliedTextInputProps, P, ErrorOfField<F[K]>, RefOfProps<P, HTMLInputElement>>
|
|
235
236
|
textInput<
|
|
236
237
|
K extends keyof StringFieldsOfFields<F>,
|
|
237
238
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -240,13 +241,14 @@ class MantineFormImpl<
|
|
|
240
241
|
valuePath: K,
|
|
241
242
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
242
243
|
TextInput: ComponentType<P> = TextInputImpl as ComponentType<P>,
|
|
243
|
-
|
|
244
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
245
|
+
): MantineFieldComponent<SuppliedTextInputProps, P, ErrorOfField<F[K]>, any> {
|
|
244
246
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
245
247
|
return this.textInputCache.retrieveOrCreate(
|
|
246
248
|
valuePath,
|
|
247
249
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
248
250
|
TextInput as ComponentType<SuppliedTextInputProps>,
|
|
249
|
-
) as MantineFieldComponent<SuppliedTextInputProps, P, ErrorOfField<F[K]>>
|
|
251
|
+
) as unknown as MantineFieldComponent<SuppliedTextInputProps, P, ErrorOfField<F[K]>>
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
valueInput<
|
|
@@ -262,7 +264,7 @@ class MantineFormImpl<
|
|
|
262
264
|
valuePath,
|
|
263
265
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
264
266
|
ValueInput as ComponentType<SuppliedValueInputProps<ValueTypeOfField<F[K]>>>,
|
|
265
|
-
) as MantineFieldComponent<
|
|
267
|
+
) as unknown as MantineFieldComponent<SuppliedValueInputProps<ValueTypeOfField<F[K]>>, P, ErrorOfField<F[K]>>
|
|
266
268
|
}
|
|
267
269
|
|
|
268
270
|
select<
|
|
@@ -273,30 +275,36 @@ class MantineFormImpl<
|
|
|
273
275
|
valuePath,
|
|
274
276
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
275
277
|
SimpleSelect as ComponentType<SuppliedValueInputProps<ValueTypeOfField<F[K]>>>,
|
|
276
|
-
) as
|
|
278
|
+
) as unknown as MantineFieldComponent<
|
|
279
|
+
SuppliedValueInputProps<ValueTypeOfField<F[K]>>,
|
|
280
|
+
ComponentProps<typeof SimpleSelect>,
|
|
281
|
+
ErrorOfField<F[K]>,
|
|
282
|
+
HTMLSelectElement
|
|
283
|
+
>
|
|
277
284
|
}
|
|
278
285
|
|
|
279
286
|
checkbox<
|
|
280
287
|
K extends keyof BooleanFieldsOfFields<F>,
|
|
281
|
-
>(valuePath: K): MantineFieldComponent<SuppliedCheckboxProps, CheckboxProps, ErrorOfField<F[K]
|
|
288
|
+
>(valuePath: K): MantineFieldComponent<SuppliedCheckboxProps, CheckboxProps, ErrorOfField<F[K]>, HTMLInputElement>
|
|
282
289
|
checkbox<
|
|
283
290
|
K extends keyof BooleanFieldsOfFields<F>,
|
|
284
291
|
P extends SuppliedCheckboxProps,
|
|
285
292
|
>(
|
|
286
293
|
valuePath: K,
|
|
287
294
|
Checkbox: ComponentType<P>,
|
|
288
|
-
): MantineFieldComponent<SuppliedCheckboxProps, P, ErrorOfField<F[K]>>
|
|
295
|
+
): MantineFieldComponent<SuppliedCheckboxProps, P, ErrorOfField<F[K]>, RefOfProps<P, HTMLInputElement>>
|
|
289
296
|
checkbox<K extends keyof BooleanFieldsOfFields<F>, P extends SuppliedCheckboxProps>(
|
|
290
297
|
valuePath: K,
|
|
291
298
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
292
299
|
Checkbox: ComponentType<P> = CheckboxImpl as ComponentType<P>,
|
|
293
|
-
|
|
300
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
301
|
+
): MantineFieldComponent<SuppliedCheckboxProps, P, ErrorOfField<F[K]>, any> {
|
|
294
302
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
295
303
|
return this.checkboxCache.retrieveOrCreate(
|
|
296
304
|
valuePath,
|
|
297
305
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
298
306
|
Checkbox as ComponentType<SuppliedCheckboxProps>,
|
|
299
|
-
) as MantineFieldComponent<SuppliedCheckboxProps, P, ErrorOfField<F[K]>>
|
|
307
|
+
) as unknown as MantineFieldComponent<SuppliedCheckboxProps, P, ErrorOfField<F[K]>>
|
|
300
308
|
}
|
|
301
309
|
|
|
302
310
|
// this should work?
|
|
@@ -324,7 +332,7 @@ class MantineFormImpl<
|
|
|
324
332
|
valuePath,
|
|
325
333
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
326
334
|
RadioGroup as ComponentType<SuppliedRadioGroupProps>,
|
|
327
|
-
) as MantineFieldComponent<SuppliedRadioGroupProps, P, ErrorOfField<F[K]>>
|
|
335
|
+
) as unknown as MantineFieldComponent<SuppliedRadioGroupProps, P, ErrorOfField<F[K]>>
|
|
328
336
|
}
|
|
329
337
|
|
|
330
338
|
radio<
|
|
@@ -356,7 +364,7 @@ class MantineFormImpl<
|
|
|
356
364
|
value,
|
|
357
365
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
358
366
|
Radio as ComponentType<SuppliedRadioProps>,
|
|
359
|
-
) as MantineFieldComponent<SuppliedRadioProps, P, ErrorOfField<F[K]>>
|
|
367
|
+
) as unknown as MantineFieldComponent<SuppliedRadioProps, P, ErrorOfField<F[K]>>
|
|
360
368
|
}
|
|
361
369
|
|
|
362
370
|
pill<
|
|
@@ -382,7 +390,7 @@ class MantineFormImpl<
|
|
|
382
390
|
valuePath,
|
|
383
391
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
384
392
|
Pill as ComponentType<SuppliedPillProps>,
|
|
385
|
-
) as MantineFieldComponent<SuppliedPillProps, P, ErrorOfField<F[K]>>
|
|
393
|
+
) as unknown as MantineFieldComponent<SuppliedPillProps, P, ErrorOfField<F[K]>>
|
|
386
394
|
}
|
|
387
395
|
|
|
388
396
|
list<
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from '@storybook/react'
|
|
6
6
|
import { type FieldsViewProps } from 'core/props'
|
|
7
7
|
import { useMantineFormFields } from 'mantine/hooks'
|
|
8
|
+
import { type Ref } from 'react'
|
|
8
9
|
import { type Field } from 'types/field'
|
|
9
10
|
import { CHECKBOX_LABEL } from './checkbox_constants'
|
|
10
11
|
|
|
@@ -13,16 +14,20 @@ function ErrorRenderer({ error }: { error: string }) {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function Component({
|
|
17
|
+
componentRef,
|
|
16
18
|
...props
|
|
17
19
|
}: FieldsViewProps<{
|
|
18
20
|
$: Field<boolean, string>,
|
|
19
|
-
}>
|
|
21
|
+
}> & {
|
|
22
|
+
componentRef: Ref<HTMLInputElement>,
|
|
23
|
+
}) {
|
|
20
24
|
const inputProps = useMantineFormFields(props)
|
|
21
25
|
const CheckboxComponent = inputProps.checkbox('$')
|
|
22
26
|
return (
|
|
23
27
|
<CheckboxComponent
|
|
24
28
|
ErrorRenderer={ErrorRenderer}
|
|
25
29
|
label={CHECKBOX_LABEL}
|
|
30
|
+
ref={componentRef}
|
|
26
31
|
/>
|
|
27
32
|
)
|
|
28
33
|
}
|
|
@@ -34,6 +39,7 @@ const meta: Meta<typeof Component> = {
|
|
|
34
39
|
onFieldFocus: action('onFieldFocus'),
|
|
35
40
|
onFieldSubmit: action('onFieldSubmit'),
|
|
36
41
|
onFieldValueChange: action('onFieldValueChange'),
|
|
42
|
+
componentRef: action('componentRef'),
|
|
37
43
|
},
|
|
38
44
|
}
|
|
39
45
|
|
|
@@ -14,7 +14,10 @@ import {
|
|
|
14
14
|
type TextInputTarget,
|
|
15
15
|
} from 'mantine/create_text_input'
|
|
16
16
|
import { useMantineFormFields } from 'mantine/hooks'
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
type ComponentType,
|
|
19
|
+
type Ref,
|
|
20
|
+
} from 'react'
|
|
18
21
|
import { type Field } from 'types/field'
|
|
19
22
|
import { TEXT_INPUT_LABEL } from './text_input_constants'
|
|
20
23
|
|
|
@@ -26,10 +29,12 @@ function ErrorRenderer({ error }: { error: string }) {
|
|
|
26
29
|
|
|
27
30
|
function Component<T extends TextInputTarget>({
|
|
28
31
|
TextInput,
|
|
32
|
+
componentRef,
|
|
29
33
|
...props
|
|
30
34
|
}: FieldsViewProps<{
|
|
31
35
|
$: Field<string, string>,
|
|
32
36
|
}> & {
|
|
37
|
+
componentRef: Ref<HTMLInputElement>,
|
|
33
38
|
TextInput?: ComponentType<StoryTextInputProps<T>>,
|
|
34
39
|
}) {
|
|
35
40
|
const form = useMantineFormFields(props)
|
|
@@ -38,6 +43,7 @@ function Component<T extends TextInputTarget>({
|
|
|
38
43
|
<TextInputComponent
|
|
39
44
|
ErrorRenderer={ErrorRenderer}
|
|
40
45
|
label={TEXT_INPUT_LABEL}
|
|
46
|
+
ref={componentRef}
|
|
41
47
|
/>
|
|
42
48
|
)
|
|
43
49
|
}
|
|
@@ -49,6 +55,7 @@ const meta: Meta<typeof Component> = {
|
|
|
49
55
|
onFieldFocus: action('onFieldFocus'),
|
|
50
56
|
onFieldSubmit: action('onFieldSubmit'),
|
|
51
57
|
onFieldValueChange: action('onFieldValueChange'),
|
|
58
|
+
componentRef: action('componentRef'),
|
|
52
59
|
},
|
|
53
60
|
}
|
|
54
61
|
|
package/mantine/types.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type ComponentType,
|
|
3
|
+
} from 'react'
|
|
2
4
|
import { type Fields } from 'types/field'
|
|
3
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type RefOfProps,
|
|
7
|
+
type UnsafePartialComponent,
|
|
8
|
+
} from 'util/partial'
|
|
4
9
|
import { type ErrorRenderer } from './error_renderer'
|
|
5
10
|
|
|
6
11
|
export type MantineForm<F extends Fields> = {
|
|
@@ -12,10 +17,13 @@ export type MantineForm<F extends Fields> = {
|
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
-
export type MantineFieldComponent<T, P = T, E = any
|
|
20
|
+
export type MantineFieldComponent<T, P = T, E = any, R = RefOfProps<P>> = UnsafePartialComponent<
|
|
16
21
|
ComponentType<P>,
|
|
17
22
|
T,
|
|
18
23
|
// escape hatch for never comparisons `E extends never` will not work, always returning never
|
|
19
24
|
// https://github.com/microsoft/TypeScript/issues/31751
|
|
20
|
-
[E] extends [never] ? {} : { ErrorRenderer: ErrorRenderer<E> }
|
|
25
|
+
[E] extends [never] ? {} : { ErrorRenderer: ErrorRenderer<E> },
|
|
26
|
+
// mantine types are too complex for us to be able to get a stable type for the ref.
|
|
27
|
+
// We can, however, do a best guess and allow overriding in the caller
|
|
28
|
+
R
|
|
21
29
|
>
|
package/package.json
CHANGED
package/util/partial.tsx
CHANGED
|
@@ -8,9 +8,13 @@ import {
|
|
|
8
8
|
forwardRef,
|
|
9
9
|
type ForwardRefExoticComponent,
|
|
10
10
|
type PropsWithoutRef,
|
|
11
|
+
type Ref,
|
|
12
|
+
type RefAttributes,
|
|
11
13
|
useMemo,
|
|
12
14
|
} from 'react'
|
|
13
15
|
|
|
16
|
+
export type RefOfProps<P, Fallback = unknown> = P extends RefAttributes<infer R> ? R : Fallback
|
|
17
|
+
|
|
14
18
|
export type PartialComponent<
|
|
15
19
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
20
|
Component extends ComponentType<any>,
|
|
@@ -27,8 +31,11 @@ export type UnsafePartialComponent<
|
|
|
27
31
|
Component extends ComponentType<any>,
|
|
28
32
|
CurriedProps,
|
|
29
33
|
AdditionalProps = {},
|
|
34
|
+
R = RefOfProps<ComponentProps<Component>>,
|
|
30
35
|
> = ForwardRefExoticComponent<
|
|
31
|
-
PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps>
|
|
36
|
+
PropsWithoutRef<RemainingComponentProps<Component, CurriedProps> & AdditionalProps> & {
|
|
37
|
+
ref?: Ref<R>,
|
|
38
|
+
}
|
|
32
39
|
>
|
|
33
40
|
|
|
34
41
|
export function createSimplePartialComponent<
|