@playfast/reform-forms 1.2.1 → 1.4.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/dist/form.d.ts +12 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +85 -0
- package/dist/form.js.map +1 -0
- package/dist/formMake.d.ts +9 -0
- package/dist/formMake.d.ts.map +1 -0
- package/dist/formMake.js +55 -0
- package/dist/formMake.js.map +1 -0
- package/dist/formReindex.d.ts +24 -0
- package/dist/formReindex.d.ts.map +1 -0
- package/dist/formReindex.js +42 -0
- package/dist/formReindex.js.map +1 -0
- package/dist/formState.d.ts +34 -0
- package/dist/formState.d.ts.map +1 -0
- package/dist/formState.js +137 -0
- package/dist/formState.js.map +1 -0
- package/dist/formTypes.d.ts +190 -0
- package/dist/formTypes.d.ts.map +1 -0
- package/dist/formTypes.js +2 -0
- package/dist/formTypes.js.map +1 -0
- package/dist/formView.d.ts +6 -0
- package/dist/formView.d.ts.map +1 -0
- package/dist/formView.js +136 -0
- package/dist/formView.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/path.d.ts +36 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +113 -0
- package/dist/path.js.map +1 -0
- package/dist/validation.d.ts +12 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +20 -0
- package/dist/validation.js.map +1 -0
- package/package.json +17 -5
- package/src/formReindex.ts +115 -0
- package/src/formState-array-keys.test.ts +179 -0
- package/src/formState-reindex.test.ts +198 -0
- package/src/formState.ts +73 -23
package/src/formState.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { Function as Fn, Option, Record } from 'effect'
|
|
2
|
-
import {
|
|
3
|
-
getNestedValue,
|
|
4
|
-
moveAt,
|
|
5
|
-
recalculateDirtyPaths,
|
|
6
|
-
replaceAt,
|
|
7
|
-
setNestedValue,
|
|
8
|
-
} from './path'
|
|
2
|
+
import { getNestedValue, moveAt, recalculateDirtyPaths, replaceAt, setNestedValue } from './path'
|
|
9
3
|
import type { FormState } from './formTypes'
|
|
4
|
+
import { movedIndexMap, reindexMetadata, removedIndexMap, swappedIndexMap } from './formReindex'
|
|
10
5
|
|
|
11
6
|
const arrayKeyCounter = { current: 0 }
|
|
12
7
|
const makeArrayKey = (): string => `form-item-${arrayKeyCounter.current++}`
|
|
@@ -103,18 +98,39 @@ const updateKeys = (
|
|
|
103
98
|
return { ...state.arrayKeys, [path]: transform(existing) }
|
|
104
99
|
}
|
|
105
100
|
|
|
106
|
-
|
|
107
|
-
|
|
101
|
+
// The subtree at `path` is being replaced, so every key describing a row inside it
|
|
102
|
+
// is about to describe nothing. Left behind, the next append folds over the stale
|
|
103
|
+
// array and hands a new row the identity of one the user discarded.
|
|
104
|
+
const withoutSubtree = (
|
|
105
|
+
keys: Readonly<Record<string, ReadonlyArray<string>>>,
|
|
106
|
+
path: string,
|
|
107
|
+
): Record<string, ReadonlyArray<string>> =>
|
|
108
|
+
Record.fromEntries(
|
|
109
|
+
Record.toEntries(keys).filter(
|
|
110
|
+
([key]) => key !== path && !key.startsWith(`${path}[`) && !key.startsWith(`${path}.`),
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
export const setAtPath = <Values>(input: SetPathInput<Values>): FormState<Values> => ({
|
|
115
|
+
...withValues(input.state, setNestedValue(input.state.values, input.path, input.value)),
|
|
116
|
+
arrayKeys: {
|
|
117
|
+
...withoutSubtree(input.state.arrayKeys, input.path),
|
|
118
|
+
...arrayKeysFor(input.value, input.path),
|
|
119
|
+
},
|
|
120
|
+
})
|
|
108
121
|
|
|
109
122
|
export const appendAtPath = <Values>(input: SetPathInput<Values>): FormState<Values> => {
|
|
110
123
|
const elements = currentArray({ source: input.state.values, path: input.path })
|
|
111
124
|
const nextValues = setNestedValue(input.state.values, input.path, [...elements, input.value])
|
|
112
125
|
return {
|
|
113
126
|
...withValues(input.state, nextValues),
|
|
114
|
-
arrayKeys:
|
|
115
|
-
...keys,
|
|
116
|
-
|
|
117
|
-
|
|
127
|
+
arrayKeys: {
|
|
128
|
+
...updateKeys(Fn.unsafeCoerce(input.state), input.path, (keys) => [...keys, makeArrayKey()]),
|
|
129
|
+
// Arrays nested inside the appended item need keys of their own — `initialState`
|
|
130
|
+
// seeds every array in the tree, and a nested list without them falls back to a
|
|
131
|
+
// positional key, so its rows change identity the first time one is touched.
|
|
132
|
+
...arrayKeysFor(input.value, `${input.path}[${elements.length}]`),
|
|
133
|
+
},
|
|
118
134
|
}
|
|
119
135
|
}
|
|
120
136
|
|
|
@@ -128,10 +144,19 @@ export const removeAtPath = <Values>(input: RemovePathInput<Values>): FormState<
|
|
|
128
144
|
input.path,
|
|
129
145
|
elements.filter((_, position) => position !== input.index),
|
|
130
146
|
)
|
|
147
|
+
const moved = reindexMetadata({
|
|
148
|
+
state: input.state,
|
|
149
|
+
path: input.path,
|
|
150
|
+
moveIndex: removedIndexMap(input.index),
|
|
151
|
+
})
|
|
131
152
|
return {
|
|
132
153
|
...withValues(input.state, nextValues),
|
|
133
|
-
|
|
134
|
-
|
|
154
|
+
touched: moved.touched,
|
|
155
|
+
errors: moved.errors,
|
|
156
|
+
arrayKeys: updateKeys(
|
|
157
|
+
Fn.unsafeCoerce({ ...input.state, arrayKeys: moved.nestedKeys }),
|
|
158
|
+
input.path,
|
|
159
|
+
(keys) => keys.filter((_, position) => position !== input.index),
|
|
135
160
|
),
|
|
136
161
|
}
|
|
137
162
|
}
|
|
@@ -143,10 +168,25 @@ export const moveAtPath = <Values>(input: MovePathInput<Values>): FormState<Valu
|
|
|
143
168
|
return input.state
|
|
144
169
|
}
|
|
145
170
|
const nextValues = setNestedValue(input.state.values, input.path, next)
|
|
171
|
+
const moved = reindexMetadata({
|
|
172
|
+
state: input.state,
|
|
173
|
+
path: input.path,
|
|
174
|
+
// `moveAt` accepts `to === elements.length` as "move it to the end", but a move
|
|
175
|
+
// keeps the length, so the item lands one slot short of that. The metadata has to
|
|
176
|
+
// follow the value, not the argument.
|
|
177
|
+
moveIndex: movedIndexMap({
|
|
178
|
+
from: input.from,
|
|
179
|
+
to: Math.min(input.to, elements.length - 1),
|
|
180
|
+
}),
|
|
181
|
+
})
|
|
146
182
|
return {
|
|
147
183
|
...withValues(input.state, nextValues),
|
|
148
|
-
|
|
149
|
-
|
|
184
|
+
touched: moved.touched,
|
|
185
|
+
errors: moved.errors,
|
|
186
|
+
arrayKeys: updateKeys(
|
|
187
|
+
Fn.unsafeCoerce({ ...input.state, arrayKeys: moved.nestedKeys }),
|
|
188
|
+
input.path,
|
|
189
|
+
(keys) => moveAt(keys, input.from, input.to),
|
|
150
190
|
),
|
|
151
191
|
}
|
|
152
192
|
}
|
|
@@ -168,14 +208,24 @@ export const swapAtPath = <Values>(input: SwapPathInput<Values>): FormState<Valu
|
|
|
168
208
|
elements[input.first],
|
|
169
209
|
)
|
|
170
210
|
const nextValues = setNestedValue(input.state.values, input.path, swapped)
|
|
211
|
+
const moved = reindexMetadata({
|
|
212
|
+
state: input.state,
|
|
213
|
+
path: input.path,
|
|
214
|
+
moveIndex: swappedIndexMap({ first: input.first, second: input.second }),
|
|
215
|
+
})
|
|
171
216
|
return {
|
|
172
217
|
...withValues(input.state, nextValues),
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
)
|
|
218
|
+
touched: moved.touched,
|
|
219
|
+
errors: moved.errors,
|
|
220
|
+
arrayKeys: updateKeys(
|
|
221
|
+
Fn.unsafeCoerce({ ...input.state, arrayKeys: moved.nestedKeys }),
|
|
222
|
+
input.path,
|
|
223
|
+
(keys) =>
|
|
224
|
+
replaceAt(
|
|
225
|
+
replaceAt(keys, input.first, keys[input.second] ?? makeArrayKey()),
|
|
226
|
+
input.second,
|
|
227
|
+
keys[input.first] ?? makeArrayKey(),
|
|
228
|
+
),
|
|
179
229
|
),
|
|
180
230
|
}
|
|
181
231
|
}
|