remix-validated-form 4.5.0-beta.1 → 4.5.0
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/.turbo/turbo-build.log +8 -8
- package/browser/internal/logic/nestedObjectToPathObject.d.ts +1 -0
- package/browser/internal/logic/nestedObjectToPathObject.js +47 -0
- package/browser/internal/state/arrayUtil.d.ts +6 -0
- package/browser/internal/state/arrayUtil.js +108 -0
- package/browser/internal/state/fieldArray.d.ts +21 -0
- package/browser/internal/state/fieldArray.js +50 -0
- package/browser/internal/state/types.d.ts +0 -0
- package/browser/internal/state/types.js +0 -0
- package/package.json +1 -1
package/.turbo/turbo-build.log
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
[2K[1G[2m$ vite build[22m
|
2
2
|
[36mvite v2.9.5 [32mbuilding for production...[36m[39m
|
3
3
|
transforming...
|
4
|
-
[32m✓[39m
|
4
|
+
[32m✓[39m 320 modules transformed.
|
5
5
|
rendering chunks...
|
6
|
-
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.cjs.js [39m [
|
7
|
-
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.cjs.js.map[39m [
|
8
|
-
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.es.js [39m [
|
9
|
-
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.es.js.map[39m [
|
10
|
-
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.umd.js [39m [
|
11
|
-
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.umd.js.map[39m [
|
6
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.cjs.js [39m [2m47.23 KiB / gzip: 17.41 KiB[22m
|
7
|
+
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.cjs.js.map[39m [2m265.34 KiB[22m
|
8
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.es.js [39m [2m104.23 KiB / gzip: 24.22 KiB[22m
|
9
|
+
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.es.js.map[39m [2m273.50 KiB[22m
|
10
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.umd.js [39m [2m47.48 KiB / gzip: 17.53 KiB[22m
|
11
|
+
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.umd.js.map[39m [2m265.31 KiB[22m
|
12
12
|
[32m[39m
|
13
13
|
[32m[36m[vite:dts][39m[32m Start generate declaration files...[39m
|
14
|
-
[32m[36m[vite:dts][39m[32m Declaration files built in
|
14
|
+
[32m[36m[vite:dts][39m[32m Declaration files built in 1961ms.[39m
|
15
15
|
[32m[39m
|
16
16
|
No name was provided for external module 'react' in output.globals – guessing 'React'
|
17
17
|
No name was provided for external module '@remix-run/react' in output.globals – guessing 'react'
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare const nestedObjectToPathObject: (val: any, acc: Record<string, any>, path: string) => any;
|
@@ -0,0 +1,47 @@
|
|
1
|
+
export const nestedObjectToPathObject = (val, acc, path) => {
|
2
|
+
if (Array.isArray(val)) {
|
3
|
+
val.forEach((v, index) => nestedObjectToPathObject(v, acc, `${path}[${index}]`));
|
4
|
+
return acc;
|
5
|
+
}
|
6
|
+
if (typeof val === "object") {
|
7
|
+
Object.entries(val).forEach(([key, value]) => {
|
8
|
+
const nextPath = path ? `${path}.${key}` : key;
|
9
|
+
nestedObjectToPathObject(value, acc, nextPath);
|
10
|
+
});
|
11
|
+
return acc;
|
12
|
+
}
|
13
|
+
if (val !== undefined) {
|
14
|
+
acc[path] = val;
|
15
|
+
}
|
16
|
+
return acc;
|
17
|
+
};
|
18
|
+
if (import.meta.vitest) {
|
19
|
+
const { describe, expect, it } = import.meta.vitest;
|
20
|
+
describe("nestedObjectToPathObject", () => {
|
21
|
+
it("should return an object with the correct path", () => {
|
22
|
+
const result = nestedObjectToPathObject({
|
23
|
+
a: 1,
|
24
|
+
b: 2,
|
25
|
+
c: { foo: "bar", baz: [true, false] },
|
26
|
+
d: [
|
27
|
+
{ foo: "bar", baz: [true, false] },
|
28
|
+
{ e: true, f: "hi" },
|
29
|
+
],
|
30
|
+
g: undefined,
|
31
|
+
}, {}, "");
|
32
|
+
expect(result).toEqual({
|
33
|
+
a: 1,
|
34
|
+
b: 2,
|
35
|
+
"c.foo": "bar",
|
36
|
+
"c.baz[0]": true,
|
37
|
+
"c.baz[1]": false,
|
38
|
+
"d[0].foo": "bar",
|
39
|
+
"d[0].baz[0]": true,
|
40
|
+
"d[0].baz[1]": false,
|
41
|
+
"d[1].e": true,
|
42
|
+
"d[1].f": "hi",
|
43
|
+
});
|
44
|
+
expect(Object.keys(result)).toHaveLength(10);
|
45
|
+
});
|
46
|
+
});
|
47
|
+
}
|
@@ -0,0 +1,6 @@
|
|
1
|
+
export declare const getArray: (values: any, field: string) => unknown[];
|
2
|
+
export declare const swap: (array: unknown[], indexA: number, indexB: number) => void;
|
3
|
+
export declare const move: (array: unknown[], from: number, to: number) => void;
|
4
|
+
export declare const insert: (array: unknown[], index: number, value: unknown) => void;
|
5
|
+
export declare const remove: (array: unknown[], index: number) => void;
|
6
|
+
export declare const replace: (array: unknown[], index: number, value: unknown) => void;
|
@@ -0,0 +1,108 @@
|
|
1
|
+
import lodashGet from "lodash/get";
|
2
|
+
import lodashSet from "lodash/set";
|
3
|
+
import invariant from "tiny-invariant";
|
4
|
+
////
|
5
|
+
// All of these array helpers are written in a way that mutates the original array.
|
6
|
+
// This is because we're working with immer.
|
7
|
+
////
|
8
|
+
export const getArray = (values, field) => {
|
9
|
+
const value = lodashGet(values, field);
|
10
|
+
if (value === undefined || value === null) {
|
11
|
+
const newValue = [];
|
12
|
+
lodashSet(values, field, newValue);
|
13
|
+
return newValue;
|
14
|
+
}
|
15
|
+
invariant(Array.isArray(value), `FieldArray: defaultValue value for ${field} must be an array, null, or undefined`);
|
16
|
+
return value;
|
17
|
+
};
|
18
|
+
export const swap = (array, indexA, indexB) => {
|
19
|
+
const itemA = array[indexA];
|
20
|
+
const itemB = array[indexB];
|
21
|
+
array[indexA] = itemB;
|
22
|
+
array[indexB] = itemA;
|
23
|
+
};
|
24
|
+
export const move = (array, from, to) => {
|
25
|
+
const [item] = array.splice(from, 1);
|
26
|
+
array.splice(to, 0, item);
|
27
|
+
};
|
28
|
+
export const insert = (array, index, value) => {
|
29
|
+
array.splice(index, 0, value);
|
30
|
+
};
|
31
|
+
export const remove = (array, index) => {
|
32
|
+
array.splice(index, 1);
|
33
|
+
};
|
34
|
+
export const replace = (array, index, value) => {
|
35
|
+
array.splice(index, 1, value);
|
36
|
+
};
|
37
|
+
if (import.meta.vitest) {
|
38
|
+
const { describe, expect, it } = import.meta.vitest;
|
39
|
+
describe("getArray", () => {
|
40
|
+
it("shoud get a deeply nested array that can be mutated to update the nested value", () => {
|
41
|
+
const values = {
|
42
|
+
d: [
|
43
|
+
{ foo: "bar", baz: [true, false] },
|
44
|
+
{ e: true, f: "hi" },
|
45
|
+
],
|
46
|
+
};
|
47
|
+
const result = getArray(values, "d[0].baz");
|
48
|
+
const finalValues = {
|
49
|
+
d: [
|
50
|
+
{ foo: "bar", baz: [true, false, true] },
|
51
|
+
{ e: true, f: "hi" },
|
52
|
+
],
|
53
|
+
};
|
54
|
+
expect(result).toEqual([true, false]);
|
55
|
+
result.push(true);
|
56
|
+
expect(values).toEqual(finalValues);
|
57
|
+
});
|
58
|
+
it("should return an empty array that can be mutated if result is null or undefined", () => {
|
59
|
+
const values = {};
|
60
|
+
const result = getArray(values, "a.foo[0].bar");
|
61
|
+
const finalValues = {
|
62
|
+
a: { foo: [{ bar: ["Bob ross"] }] },
|
63
|
+
};
|
64
|
+
expect(result).toEqual([]);
|
65
|
+
result.push("Bob ross");
|
66
|
+
expect(values).toEqual(finalValues);
|
67
|
+
});
|
68
|
+
it("should throw if the value is defined and not an array", () => {
|
69
|
+
const values = { foo: "foo" };
|
70
|
+
expect(() => getArray(values, "foo")).toThrow();
|
71
|
+
});
|
72
|
+
});
|
73
|
+
describe("swap", () => {
|
74
|
+
it("should swap two items", () => {
|
75
|
+
const array = [1, 2, 3];
|
76
|
+
swap(array, 0, 1);
|
77
|
+
expect(array).toEqual([2, 1, 3]);
|
78
|
+
});
|
79
|
+
});
|
80
|
+
describe("move", () => {
|
81
|
+
it("should move an item to a new index", () => {
|
82
|
+
const array = [1, 2, 3];
|
83
|
+
move(array, 0, 1);
|
84
|
+
expect(array).toEqual([2, 1, 3]);
|
85
|
+
});
|
86
|
+
});
|
87
|
+
describe("insert", () => {
|
88
|
+
it("should insert an item at a new index", () => {
|
89
|
+
const array = [1, 2, 3];
|
90
|
+
insert(array, 1, 4);
|
91
|
+
expect(array).toEqual([1, 4, 2, 3]);
|
92
|
+
});
|
93
|
+
});
|
94
|
+
describe("remove", () => {
|
95
|
+
it("should remove an item at a given index", () => {
|
96
|
+
const array = [1, 2, 3];
|
97
|
+
remove(array, 1);
|
98
|
+
expect(array).toEqual([1, 3]);
|
99
|
+
});
|
100
|
+
});
|
101
|
+
describe("replace", () => {
|
102
|
+
it("should replace an item at a given index", () => {
|
103
|
+
const array = [1, 2, 3];
|
104
|
+
replace(array, 1, 4);
|
105
|
+
expect(array).toEqual([1, 4, 3]);
|
106
|
+
});
|
107
|
+
});
|
108
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import React from "react";
|
2
|
+
export declare const FieldArrayContext: React.Context<{
|
3
|
+
defaultValues: any[];
|
4
|
+
name: string;
|
5
|
+
} | null>;
|
6
|
+
export declare type FieldArrayHelpers = {
|
7
|
+
push: (item: any) => void;
|
8
|
+
swap: (indexA: number, indexB: number) => void;
|
9
|
+
move: (from: number, to: number) => void;
|
10
|
+
insert: (index: number, value: any) => void;
|
11
|
+
unshift: () => void;
|
12
|
+
remove: (index: number) => void;
|
13
|
+
pop: () => void;
|
14
|
+
replace: (index: number, value: any) => void;
|
15
|
+
};
|
16
|
+
export declare type FieldArrayProps = {
|
17
|
+
name: string;
|
18
|
+
children: (itemDefaults: any[], helpers: FieldArrayHelpers) => React.ReactNode;
|
19
|
+
formId?: string;
|
20
|
+
};
|
21
|
+
export declare const FieldArray: ({ name, children, formId }: FieldArrayProps) => JSX.Element;
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { useMemo, createContext } from "react";
|
3
|
+
import invariant from "tiny-invariant";
|
4
|
+
import { useInternalFormContext } from "../hooks";
|
5
|
+
import { useControllableValue } from "./controlledFields";
|
6
|
+
import { useFormStore } from "./storeHooks";
|
7
|
+
const useFieldArray = (context, field) => {
|
8
|
+
// TODO: Fieldarrays need to handle/update these things, too:
|
9
|
+
// - touchedFields & fieldErrors should be updated when fields are added/removed
|
10
|
+
// - Could probably move some of these callbacks into the store
|
11
|
+
// - There's a bug where adding a new field to the fieldarray validates the new field.
|
12
|
+
// - For some reason this only happens in the test-app, but not in the docs app.
|
13
|
+
const [value] = useControllableValue(context, field);
|
14
|
+
const arr = useFormStore(context.formId, (state) => state.controlledFields.array);
|
15
|
+
const helpers = useMemo(() => ({
|
16
|
+
push: (item) => {
|
17
|
+
arr.push(field, item);
|
18
|
+
},
|
19
|
+
swap: (indexA, indexB) => {
|
20
|
+
arr.swap(field, indexA, indexB);
|
21
|
+
},
|
22
|
+
move: (from, to) => {
|
23
|
+
arr.move(field, from, to);
|
24
|
+
},
|
25
|
+
insert: (index, value) => {
|
26
|
+
arr.insert(field, index, value);
|
27
|
+
},
|
28
|
+
unshift: () => {
|
29
|
+
arr.unshift(field);
|
30
|
+
},
|
31
|
+
remove: (index) => {
|
32
|
+
arr.remove(field, index);
|
33
|
+
},
|
34
|
+
pop: () => {
|
35
|
+
arr.pop(field);
|
36
|
+
},
|
37
|
+
replace: (index, value) => {
|
38
|
+
arr.replace(field, index, value);
|
39
|
+
},
|
40
|
+
}), [arr, field]);
|
41
|
+
return [value, helpers];
|
42
|
+
};
|
43
|
+
export const FieldArrayContext = createContext(null);
|
44
|
+
export const FieldArray = ({ name, children, formId }) => {
|
45
|
+
const context = useInternalFormContext(formId, "FieldArray");
|
46
|
+
const [value, helpers] = useFieldArray(context, name);
|
47
|
+
invariant(value === undefined || value === null || Array.isArray(value), `FieldArray: defaultValue value for ${name} must be an array, null, or undefined`);
|
48
|
+
const contextValue = useMemo(() => ({ defaultValues: value !== null && value !== void 0 ? value : [], name }), [name, value]);
|
49
|
+
return (_jsx(FieldArrayContext.Provider, { value: contextValue, children: children(contextValue.defaultValues, helpers) }, void 0));
|
50
|
+
};
|
File without changes
|
File without changes
|
package/package.json
CHANGED