@strictly/react-form 0.0.27 → 0.0.29
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 +3 -1
- package/.out/core/mobx/form_model.js +50 -100
- package/.out/core/mobx/specs/form_model.tests.js +14 -12
- package/.out/mantine/create_list.d.ts +2 -1
- package/.out/mantine/create_list.js +15 -3
- package/.out/mantine/specs/list_hooks.stories.js +8 -0
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.out/types/field.d.ts +1 -0
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-check-types.log +1 -1
- package/core/mobx/form_model.ts +48 -115
- package/core/mobx/specs/form_model.tests.ts +14 -12
- package/dist/index.cjs +80 -115
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +71 -105
- package/mantine/create_list.tsx +27 -3
- package/mantine/specs/__snapshots__/list_hooks.tests.tsx.snap +3 -3
- package/mantine/specs/list_hooks.stories.tsx +8 -0
- package/package.json +1 -1
- package/types/field.ts +1 -0
package/.out/types/field.d.ts
CHANGED
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
|
+
[32mESM[39m [1mdist/index.js [22m[32m58.89 KB[39m
|
|
11
|
+
[32mESM[39m ⚡️ Build success in 141ms
|
|
12
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m63.00 KB[39m
|
|
13
|
+
[32mCJS[39m ⚡️ Build success in 145ms
|
|
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 31686ms
|
|
16
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m38.21 KB[39m
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m38.21 KB[39m
|
|
18
|
+
Done in 32.91s.
|
package/core/mobx/form_model.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
assertExists,
|
|
3
3
|
assertExistsAndReturn,
|
|
4
|
-
assertState,
|
|
5
4
|
checkValidNumber,
|
|
6
5
|
type ElementOfArray,
|
|
7
6
|
map,
|
|
@@ -161,13 +160,16 @@ export abstract class FormModel<
|
|
|
161
160
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
162
161
|
private readonly originalValues: Record<string, any>
|
|
163
162
|
|
|
163
|
+
// maintains the value paths of lists when the original order is destroyed by deletes or reordering
|
|
164
|
+
private readonly listIndicesToKeys: Record<string, number[]> = {}
|
|
165
|
+
|
|
164
166
|
constructor(
|
|
165
167
|
readonly type: T,
|
|
166
|
-
originalValue: ValueOfType<ReadonlyTypeOfType<T>>,
|
|
168
|
+
private readonly originalValue: ValueOfType<ReadonlyTypeOfType<T>>,
|
|
167
169
|
protected readonly adapters: TypePathsToAdapters,
|
|
168
170
|
protected readonly mode: FormMode,
|
|
169
171
|
) {
|
|
170
|
-
this.originalValues = flattenValuesOfType<ReadonlyTypeOfType<T>>(type, originalValue)
|
|
172
|
+
this.originalValues = flattenValuesOfType<ReadonlyTypeOfType<T>>(type, originalValue, this.listIndicesToKeys)
|
|
171
173
|
this.value = mobxCopy(type, originalValue)
|
|
172
174
|
this.flattenedTypeDefs = flattenTypesOfType(type)
|
|
173
175
|
// pre-populate field overrides for consistent behavior when default information is overwritten
|
|
@@ -202,6 +204,7 @@ export abstract class FormModel<
|
|
|
202
204
|
// cannot call this.context yet as the "this" pointer has not been fully created
|
|
203
205
|
return convert(fieldValue, valuePath, contextValue)
|
|
204
206
|
},
|
|
207
|
+
this.listIndicesToKeys,
|
|
205
208
|
)
|
|
206
209
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
207
210
|
this.fieldOverrides = map(conversions, function (_k, v) {
|
|
@@ -260,6 +263,7 @@ export abstract class FormModel<
|
|
|
260
263
|
typePath as keyof TypePathsToAdapters,
|
|
261
264
|
)
|
|
262
265
|
},
|
|
266
|
+
this.listIndicesToKeys,
|
|
263
267
|
)
|
|
264
268
|
}
|
|
265
269
|
|
|
@@ -335,7 +339,10 @@ export abstract class FormModel<
|
|
|
335
339
|
}
|
|
336
340
|
}
|
|
337
341
|
|
|
338
|
-
private synthesizeFieldByPaths(
|
|
342
|
+
private synthesizeFieldByPaths(
|
|
343
|
+
valuePath: keyof ValuePathsToAdapters,
|
|
344
|
+
typePath: keyof TypePathsToAdapters,
|
|
345
|
+
): Field | undefined {
|
|
339
346
|
const field = this.getField(valuePath, typePath)
|
|
340
347
|
if (field == null) {
|
|
341
348
|
return
|
|
@@ -391,6 +398,9 @@ export abstract class FormModel<
|
|
|
391
398
|
error,
|
|
392
399
|
readonly: readonly && !this.forceMutableFields,
|
|
393
400
|
required,
|
|
401
|
+
// make a copy of the index mapping and remove the final value (next id)
|
|
402
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
403
|
+
listIndexToKey: this.listIndicesToKeys[valuePath as string]?.slice(0, -1),
|
|
394
404
|
}
|
|
395
405
|
}
|
|
396
406
|
|
|
@@ -408,6 +418,7 @@ export abstract class FormModel<
|
|
|
408
418
|
(value: ValueOfType<T>): void => {
|
|
409
419
|
this.value = mobxCopy(this.type, value)
|
|
410
420
|
},
|
|
421
|
+
this.listIndicesToKeys,
|
|
411
422
|
)
|
|
412
423
|
}
|
|
413
424
|
|
|
@@ -434,6 +445,12 @@ export abstract class FormModel<
|
|
|
434
445
|
})
|
|
435
446
|
}
|
|
436
447
|
|
|
448
|
+
@computed
|
|
449
|
+
get valueChanged() {
|
|
450
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
451
|
+
return !equals(this.type, this.value, this.originalValue as ValueOfType<T>)
|
|
452
|
+
}
|
|
453
|
+
|
|
437
454
|
typePath<K extends keyof ValueToTypePaths>(valuePath: K): ValueToTypePaths[K] {
|
|
438
455
|
return valuePathToTypePath<ValueToTypePaths, K>(this.type, valuePath, true)
|
|
439
456
|
}
|
|
@@ -482,141 +499,57 @@ export abstract class FormModel<
|
|
|
482
499
|
element,
|
|
483
500
|
...originalList.slice(definedIndex),
|
|
484
501
|
]
|
|
485
|
-
// shuffle the overrides around to account for new indices
|
|
486
|
-
// to so this we need to sort the array indices in descending order
|
|
487
|
-
const targetPaths = Object.keys(this.fieldOverrides).filter(function (v) {
|
|
488
|
-
return v.startsWith(`${listValuePath}.`)
|
|
489
|
-
}).map(function (v) {
|
|
490
|
-
const parts = v.substring(listValuePath.length + 1).split('.')
|
|
491
|
-
const index = parseInt(parts[0])
|
|
492
|
-
return [
|
|
493
|
-
index,
|
|
494
|
-
parts.slice(1),
|
|
495
|
-
] as const
|
|
496
|
-
}).filter(function ([index]) {
|
|
497
|
-
return index >= definedIndex
|
|
498
|
-
}).sort(function ([a], [b]) {
|
|
499
|
-
// descending
|
|
500
|
-
return b - a
|
|
501
|
-
})
|
|
502
502
|
runInAction(() => {
|
|
503
|
-
targetPaths.forEach(([
|
|
504
|
-
index,
|
|
505
|
-
postfix,
|
|
506
|
-
]) => {
|
|
507
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
508
|
-
const fromJsonPath = [
|
|
509
|
-
listValuePath,
|
|
510
|
-
`${index}`,
|
|
511
|
-
...postfix,
|
|
512
|
-
].join('.') as keyof ValuePathsToAdapters
|
|
513
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
514
|
-
const toJsonPath = [
|
|
515
|
-
listValuePath,
|
|
516
|
-
`${index + 1}`,
|
|
517
|
-
...postfix,
|
|
518
|
-
].join('.') as keyof ValuePathsToAdapters
|
|
519
|
-
const fieldOverride = this.fieldOverrides[fromJsonPath]
|
|
520
|
-
delete this.fieldOverrides[fromJsonPath]
|
|
521
|
-
this.fieldOverrides[toJsonPath] = fieldOverride
|
|
522
|
-
const validation = this.validation[fromJsonPath]
|
|
523
|
-
delete this.validation[fromJsonPath]
|
|
524
|
-
this.validation[toJsonPath] = validation
|
|
525
|
-
})
|
|
526
503
|
accessor.set(newList)
|
|
527
504
|
// delete any value overrides so the new list isn't shadowed
|
|
528
505
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
529
506
|
delete this.fieldOverrides[listValuePath as keyof ValuePathsToAdapters]
|
|
507
|
+
const indicesToKeys = assertExistsAndReturn(
|
|
508
|
+
this.listIndicesToKeys[listValuePath],
|
|
509
|
+
'no index to key mapping for list {}',
|
|
510
|
+
listValuePath,
|
|
511
|
+
)
|
|
512
|
+
const nextKey = indicesToKeys[indicesToKeys.length - 1]
|
|
513
|
+
// insert the next key
|
|
514
|
+
indicesToKeys.splice(definedIndex, 0, nextKey)
|
|
515
|
+
// create the new next key
|
|
516
|
+
indicesToKeys[indicesToKeys.length - 1] = nextKey + 1
|
|
530
517
|
})
|
|
531
518
|
}
|
|
532
519
|
|
|
533
520
|
removeListItem<K extends keyof FlattenedListTypesOfType<T>>(...elementValuePaths: readonly `${K}.${number}`[]) {
|
|
534
|
-
// sort and reverse so we delete last to first so indices of sequential deletions are preserved
|
|
535
|
-
const orderedElementValuePaths = elementValuePaths.toSorted().reverse()
|
|
536
521
|
runInAction(() => {
|
|
537
|
-
|
|
522
|
+
elementValuePaths.forEach(elementValuePath => {
|
|
538
523
|
const [
|
|
539
524
|
listValuePath,
|
|
540
|
-
|
|
525
|
+
elementKeyString,
|
|
541
526
|
] = assertExistsAndReturn(
|
|
542
527
|
jsonPathPop(elementValuePath),
|
|
543
528
|
'expected a path with two or more segments {}',
|
|
544
529
|
elementValuePath,
|
|
545
530
|
)
|
|
546
531
|
const accessor = this.accessors[listValuePath]
|
|
547
|
-
const
|
|
548
|
-
parseInt(
|
|
549
|
-
'unexpected
|
|
550
|
-
|
|
532
|
+
const elementKey = checkValidNumber(
|
|
533
|
+
parseInt(elementKeyString),
|
|
534
|
+
'unexpected id {} ({})',
|
|
535
|
+
elementKeyString,
|
|
551
536
|
elementValuePath,
|
|
552
537
|
)
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
elementIndex,
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
newList.splice(elementIndex, 1)
|
|
561
|
-
|
|
562
|
-
// shuffle the overrides around to account for new indices
|
|
563
|
-
// to so this we need to sort the array indices in descending order
|
|
564
|
-
const targetPaths = Object.keys(this.fieldOverrides).filter(function (v) {
|
|
565
|
-
return v.startsWith(`${listValuePath}.`)
|
|
566
|
-
}).map(function (v) {
|
|
567
|
-
const parts = v.substring(listValuePath.length + 1).split('.')
|
|
568
|
-
const index = parseInt(parts[0])
|
|
569
|
-
return [
|
|
570
|
-
index,
|
|
571
|
-
parts.slice(1),
|
|
572
|
-
] as const
|
|
573
|
-
}).filter(function ([index]) {
|
|
574
|
-
return index > elementIndex
|
|
575
|
-
}).sort(function ([a], [b]) {
|
|
576
|
-
// descending
|
|
577
|
-
return a - b
|
|
578
|
-
})
|
|
579
|
-
|
|
580
|
-
targetPaths.forEach(([
|
|
581
|
-
index,
|
|
582
|
-
postfix,
|
|
583
|
-
]) => {
|
|
584
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
585
|
-
const fromJsonPath = [
|
|
586
|
-
listValuePath,
|
|
587
|
-
`${index}`,
|
|
588
|
-
...postfix,
|
|
589
|
-
].join('.') as keyof ValuePathsToAdapters
|
|
538
|
+
const indicesToKeys = this.listIndicesToKeys[listValuePath]
|
|
539
|
+
const elementIndex = indicesToKeys?.indexOf(elementKey) ?? -1
|
|
540
|
+
if (elementIndex >= 0) {
|
|
541
|
+
const newList = [...accessor.value]
|
|
542
|
+
newList.splice(elementIndex, 1)
|
|
543
|
+
accessor.set(newList)
|
|
544
|
+
// delete any value overrides so the new list isn't shadowed
|
|
590
545
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
...postfix,
|
|
595
|
-
].join('.') as keyof ValuePathsToAdapters
|
|
596
|
-
const fieldOverride = this.fieldOverrides[fromJsonPath]
|
|
597
|
-
delete this.fieldOverrides[fromJsonPath]
|
|
598
|
-
this.fieldOverrides[toJsonPath] = fieldOverride
|
|
599
|
-
const validation = this.validation[fromJsonPath]
|
|
600
|
-
delete this.validation[fromJsonPath]
|
|
601
|
-
this.validation[toJsonPath] = validation
|
|
602
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
|
|
603
|
-
this.moveListItem(fromJsonPath as any, toJsonPath as any)
|
|
604
|
-
})
|
|
605
|
-
accessor.set(newList)
|
|
606
|
-
// delete any value overrides so the new list isn't shadowed
|
|
607
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
608
|
-
delete this.fieldOverrides[listValuePath as keyof ValuePathsToAdapters]
|
|
546
|
+
delete this.fieldOverrides[listValuePath as keyof ValuePathsToAdapters]
|
|
547
|
+
indicesToKeys.splice(elementIndex, 1)
|
|
548
|
+
}
|
|
609
549
|
})
|
|
610
550
|
})
|
|
611
551
|
}
|
|
612
552
|
|
|
613
|
-
protected moveListItem<K extends keyof FlattenedListTypesOfType<T>>(fromValuePath: K, toValuePath: K) {
|
|
614
|
-
// do nothing, this is for subclasses to override
|
|
615
|
-
// put in some nonsense so TS doesn't complain about the parameters not being used
|
|
616
|
-
fromValuePath satisfies K
|
|
617
|
-
toValuePath satisfies K
|
|
618
|
-
}
|
|
619
|
-
|
|
620
553
|
private internalSetFieldValue<K extends keyof ValuePathsToAdapters>(
|
|
621
554
|
valuePath: K,
|
|
622
555
|
value: ToOfFieldAdapter<ValuePathsToAdapters[K]>,
|
|
@@ -715,7 +648,7 @@ export abstract class FormModel<
|
|
|
715
648
|
}
|
|
716
649
|
|
|
717
650
|
isValuePathActive<K extends keyof ValuePathsToAdapters>(valuePath: K): boolean {
|
|
718
|
-
const values = flattenValuesOfType(this.type, this.value)
|
|
651
|
+
const values = flattenValuesOfType(this.type, this.value, this.listIndicesToKeys)
|
|
719
652
|
const keys = new Set(Object.keys(values))
|
|
720
653
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
721
654
|
return keys.has(valuePath as string)
|
|
@@ -828,19 +828,20 @@ describe('all', function () {
|
|
|
828
828
|
|
|
829
829
|
it.each([
|
|
830
830
|
[
|
|
831
|
-
|
|
831
|
+
// new
|
|
832
|
+
'$.3',
|
|
832
833
|
'0',
|
|
833
834
|
],
|
|
834
835
|
[
|
|
835
|
-
'$.
|
|
836
|
+
'$.0',
|
|
836
837
|
'x',
|
|
837
838
|
],
|
|
838
839
|
[
|
|
839
|
-
'$.
|
|
840
|
+
'$.1',
|
|
840
841
|
'3',
|
|
841
842
|
],
|
|
842
843
|
[
|
|
843
|
-
'$.
|
|
844
|
+
'$.2',
|
|
844
845
|
'z',
|
|
845
846
|
],
|
|
846
847
|
] as const)('it reports the value of field %s as %s', function (path, fieldValue) {
|
|
@@ -849,19 +850,20 @@ describe('all', function () {
|
|
|
849
850
|
|
|
850
851
|
it.each([
|
|
851
852
|
[
|
|
852
|
-
|
|
853
|
+
// new
|
|
854
|
+
'$.3',
|
|
853
855
|
undefined,
|
|
854
856
|
],
|
|
855
857
|
[
|
|
856
|
-
'$.
|
|
858
|
+
'$.0',
|
|
857
859
|
IS_NAN_ERROR,
|
|
858
860
|
],
|
|
859
861
|
[
|
|
860
|
-
'$.
|
|
862
|
+
'$.1',
|
|
861
863
|
undefined,
|
|
862
864
|
],
|
|
863
865
|
[
|
|
864
|
-
'$.
|
|
866
|
+
'$.2',
|
|
865
867
|
IS_NAN_ERROR,
|
|
866
868
|
],
|
|
867
869
|
] as const)('it reports the error of field %s', function (path, error) {
|
|
@@ -926,11 +928,11 @@ describe('all', function () {
|
|
|
926
928
|
|
|
927
929
|
it('updates the field values and errors', function () {
|
|
928
930
|
expect(model.fields).toEqual({
|
|
929
|
-
'$.
|
|
931
|
+
'$.1': expect.objectContaining({
|
|
930
932
|
value: '3',
|
|
931
933
|
error: undefined,
|
|
932
934
|
}),
|
|
933
|
-
'$.
|
|
935
|
+
'$.2': expect.objectContaining({
|
|
934
936
|
value: 'z',
|
|
935
937
|
error: IS_NAN_ERROR,
|
|
936
938
|
}),
|
|
@@ -956,7 +958,7 @@ describe('all', function () {
|
|
|
956
958
|
value: 'x',
|
|
957
959
|
error: IS_NAN_ERROR,
|
|
958
960
|
}),
|
|
959
|
-
'$.
|
|
961
|
+
'$.2': expect.objectContaining({
|
|
960
962
|
value: 'z',
|
|
961
963
|
error: IS_NAN_ERROR,
|
|
962
964
|
}),
|
|
@@ -975,7 +977,7 @@ describe('all', function () {
|
|
|
975
977
|
|
|
976
978
|
it('updates the field values and errors', function () {
|
|
977
979
|
expect(model.fields).toEqual({
|
|
978
|
-
'$.
|
|
980
|
+
'$.2': expect.objectContaining({
|
|
979
981
|
value: 'z',
|
|
980
982
|
error: IS_NAN_ERROR,
|
|
981
983
|
}),
|