@formality-ui/react 0.0.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/README.md +441 -0
- package/dist/index.cjs +1047 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +860 -0
- package/dist/index.d.ts +860 -0
- package/dist/index.js +1028 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1028 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo, useRef, useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { useForm, FormProvider, useWatch, Controller, useFormContext as useFormContext$1 } from 'react-hook-form';
|
|
4
|
+
import { debounce } from 'lodash-es';
|
|
5
|
+
import { resolveAllInitialValues, buildFormContext, evaluateDescriptor, resolveFormTitle, transformFieldName, extractValueField, inferFieldsFromDescriptor, inferFieldsFromConditions, evaluateConditions, buildFieldContext, resolveInputConfig, resolveLabel, runValidator, resolveErrorMessage, parse, format, mergeFieldProps, sortFieldsByOrder } from '@formality-ui/core';
|
|
6
|
+
|
|
7
|
+
// src/context/ConfigContext.ts
|
|
8
|
+
var defaultConfigContext = {
|
|
9
|
+
inputs: {},
|
|
10
|
+
formatters: {},
|
|
11
|
+
parsers: {},
|
|
12
|
+
validators: {},
|
|
13
|
+
errorMessages: {},
|
|
14
|
+
inputTemplates: {},
|
|
15
|
+
defaultSubscriptionPropName: "state",
|
|
16
|
+
defaultFieldProps: {}
|
|
17
|
+
};
|
|
18
|
+
var ConfigContext = createContext(defaultConfigContext);
|
|
19
|
+
ConfigContext.displayName = "FormalityConfigContext";
|
|
20
|
+
function useConfigContext() {
|
|
21
|
+
return useContext(ConfigContext);
|
|
22
|
+
}
|
|
23
|
+
var FormContext = createContext(null);
|
|
24
|
+
FormContext.displayName = "FormalityFormContext";
|
|
25
|
+
function useFormContext() {
|
|
26
|
+
const context = useContext(FormContext);
|
|
27
|
+
if (!context) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
"useFormContext must be used within a Form component. Make sure your component is wrapped in a <Form> component."
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
return context;
|
|
33
|
+
}
|
|
34
|
+
var defaultGroupContext = {
|
|
35
|
+
state: {
|
|
36
|
+
isDisabled: false,
|
|
37
|
+
isVisible: true,
|
|
38
|
+
hasSetCondition: false,
|
|
39
|
+
setValue: void 0,
|
|
40
|
+
conditions: [],
|
|
41
|
+
subscriptions: []
|
|
42
|
+
},
|
|
43
|
+
subscriptions: [],
|
|
44
|
+
inferredInputs: [],
|
|
45
|
+
config: { conditions: [], subscribesTo: [] }
|
|
46
|
+
};
|
|
47
|
+
var GroupContext = createContext(defaultGroupContext);
|
|
48
|
+
GroupContext.displayName = "FormalityGroupContext";
|
|
49
|
+
function useGroupContext() {
|
|
50
|
+
return useContext(GroupContext);
|
|
51
|
+
}
|
|
52
|
+
function FormalityProvider({
|
|
53
|
+
children,
|
|
54
|
+
inputs,
|
|
55
|
+
formatters = {},
|
|
56
|
+
parsers = {},
|
|
57
|
+
validators = {},
|
|
58
|
+
errorMessages = {},
|
|
59
|
+
defaultInputTemplate,
|
|
60
|
+
inputTemplates = {},
|
|
61
|
+
defaultSubscriptionPropName = "state",
|
|
62
|
+
defaultFieldProps = {},
|
|
63
|
+
selectDefaultFieldProps
|
|
64
|
+
}) {
|
|
65
|
+
const contextValue = useMemo(
|
|
66
|
+
() => ({
|
|
67
|
+
inputs,
|
|
68
|
+
formatters,
|
|
69
|
+
parsers,
|
|
70
|
+
validators,
|
|
71
|
+
errorMessages,
|
|
72
|
+
defaultInputTemplate,
|
|
73
|
+
inputTemplates,
|
|
74
|
+
defaultSubscriptionPropName,
|
|
75
|
+
defaultFieldProps,
|
|
76
|
+
selectDefaultFieldProps
|
|
77
|
+
}),
|
|
78
|
+
[
|
|
79
|
+
inputs,
|
|
80
|
+
formatters,
|
|
81
|
+
parsers,
|
|
82
|
+
validators,
|
|
83
|
+
errorMessages,
|
|
84
|
+
defaultInputTemplate,
|
|
85
|
+
inputTemplates,
|
|
86
|
+
defaultSubscriptionPropName,
|
|
87
|
+
defaultFieldProps,
|
|
88
|
+
selectDefaultFieldProps
|
|
89
|
+
]
|
|
90
|
+
);
|
|
91
|
+
return /* @__PURE__ */ jsx(ConfigContext.Provider, { value: contextValue, children });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/utils/makeProxyState.ts
|
|
95
|
+
function makeProxyState(source) {
|
|
96
|
+
const result = {};
|
|
97
|
+
for (const key in source) {
|
|
98
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
99
|
+
Object.defineProperty(result, key, {
|
|
100
|
+
get: () => source[key],
|
|
101
|
+
enumerable: true,
|
|
102
|
+
configurable: true
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
function makeDeepProxyState(source) {
|
|
109
|
+
const result = {};
|
|
110
|
+
for (const key in source) {
|
|
111
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
112
|
+
source[key];
|
|
113
|
+
Object.defineProperty(result, key, {
|
|
114
|
+
get: () => {
|
|
115
|
+
const currentValue = source[key];
|
|
116
|
+
if (currentValue !== null && typeof currentValue === "object" && !Array.isArray(currentValue)) {
|
|
117
|
+
return makeDeepProxyState(currentValue);
|
|
118
|
+
}
|
|
119
|
+
return currentValue;
|
|
120
|
+
},
|
|
121
|
+
enumerable: true,
|
|
122
|
+
configurable: true
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
var defaultGroupContext2 = {
|
|
129
|
+
state: {
|
|
130
|
+
isDisabled: false,
|
|
131
|
+
isVisible: true,
|
|
132
|
+
hasSetCondition: false,
|
|
133
|
+
setValue: void 0,
|
|
134
|
+
conditions: [],
|
|
135
|
+
subscriptions: []
|
|
136
|
+
},
|
|
137
|
+
subscriptions: [],
|
|
138
|
+
inferredInputs: [],
|
|
139
|
+
config: { conditions: [], subscribesTo: [] }
|
|
140
|
+
};
|
|
141
|
+
function Form({
|
|
142
|
+
children,
|
|
143
|
+
config,
|
|
144
|
+
formConfig = {},
|
|
145
|
+
onSubmit,
|
|
146
|
+
record,
|
|
147
|
+
autoSave = false,
|
|
148
|
+
debounce: debounceMs = 1e3,
|
|
149
|
+
validate
|
|
150
|
+
}) {
|
|
151
|
+
const providerConfig = useConfigContext();
|
|
152
|
+
const mergedInputs = useMemo(() => {
|
|
153
|
+
const formInputs = typeof formConfig.inputs === "function" ? formConfig.inputs(providerConfig.inputs) : formConfig.inputs ?? {};
|
|
154
|
+
const result = { ...providerConfig.inputs };
|
|
155
|
+
for (const [key, override] of Object.entries(formInputs)) {
|
|
156
|
+
if (result[key]) {
|
|
157
|
+
result[key] = { ...result[key], ...override };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}, [providerConfig.inputs, formConfig.inputs]);
|
|
162
|
+
const defaultValues = useMemo(() => {
|
|
163
|
+
return resolveAllInitialValues(config, mergedInputs, record ?? {});
|
|
164
|
+
}, [config, mergedInputs, record]);
|
|
165
|
+
const methods = useForm({
|
|
166
|
+
mode: "onChange",
|
|
167
|
+
defaultValues,
|
|
168
|
+
values: record
|
|
169
|
+
});
|
|
170
|
+
const fieldRegistry = useRef(/* @__PURE__ */ new Set());
|
|
171
|
+
const [registeredFields, setRegisteredFields] = useState(
|
|
172
|
+
/* @__PURE__ */ new Set()
|
|
173
|
+
);
|
|
174
|
+
const invertedSubscriptions = useRef(/* @__PURE__ */ new Map());
|
|
175
|
+
const watcherSetters = useRef(/* @__PURE__ */ new Map());
|
|
176
|
+
const pendingWatcherUpdates = useRef(/* @__PURE__ */ new Map());
|
|
177
|
+
const validatingFields = useRef(/* @__PURE__ */ new Map());
|
|
178
|
+
const pendingChangedFields = useRef(/* @__PURE__ */ new Set());
|
|
179
|
+
const pendingAffectedFields = useRef(/* @__PURE__ */ new Set());
|
|
180
|
+
const executionVersionRef = useRef(0);
|
|
181
|
+
const registerField = useCallback((name) => {
|
|
182
|
+
fieldRegistry.current.add(name);
|
|
183
|
+
setRegisteredFields(new Set(fieldRegistry.current));
|
|
184
|
+
}, []);
|
|
185
|
+
const unregisterField = useCallback((name) => {
|
|
186
|
+
fieldRegistry.current.delete(name);
|
|
187
|
+
setRegisteredFields(new Set(fieldRegistry.current));
|
|
188
|
+
}, []);
|
|
189
|
+
const addSubscription = useCallback((target, subscriber) => {
|
|
190
|
+
if (!invertedSubscriptions.current.has(target)) {
|
|
191
|
+
invertedSubscriptions.current.set(target, /* @__PURE__ */ new Set());
|
|
192
|
+
}
|
|
193
|
+
invertedSubscriptions.current.get(target).add(subscriber);
|
|
194
|
+
const setter = watcherSetters.current.get(target);
|
|
195
|
+
if (setter) {
|
|
196
|
+
setter((prev) => ({ ...prev, [subscriber]: true }));
|
|
197
|
+
} else {
|
|
198
|
+
if (!pendingWatcherUpdates.current.has(target)) {
|
|
199
|
+
pendingWatcherUpdates.current.set(target, /* @__PURE__ */ new Set());
|
|
200
|
+
}
|
|
201
|
+
pendingWatcherUpdates.current.get(target).add(subscriber);
|
|
202
|
+
}
|
|
203
|
+
}, []);
|
|
204
|
+
const removeSubscription = useCallback(
|
|
205
|
+
(target, subscriber) => {
|
|
206
|
+
invertedSubscriptions.current.get(target)?.delete(subscriber);
|
|
207
|
+
const setter = watcherSetters.current.get(target);
|
|
208
|
+
if (setter) {
|
|
209
|
+
setter((prev) => {
|
|
210
|
+
const next = { ...prev };
|
|
211
|
+
delete next[subscriber];
|
|
212
|
+
return next;
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
[]
|
|
217
|
+
);
|
|
218
|
+
const registerWatcherSetter = useCallback(
|
|
219
|
+
(name, setter) => {
|
|
220
|
+
watcherSetters.current.set(name, setter);
|
|
221
|
+
const pending = pendingWatcherUpdates.current.get(name);
|
|
222
|
+
if (pending?.size) {
|
|
223
|
+
setter((prev) => {
|
|
224
|
+
const next = { ...prev };
|
|
225
|
+
pending.forEach((sub) => {
|
|
226
|
+
next[sub] = true;
|
|
227
|
+
});
|
|
228
|
+
return next;
|
|
229
|
+
});
|
|
230
|
+
pendingWatcherUpdates.current.delete(name);
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
[]
|
|
234
|
+
);
|
|
235
|
+
const unregisterWatcherSetter = useCallback((name) => {
|
|
236
|
+
watcherSetters.current.delete(name);
|
|
237
|
+
}, []);
|
|
238
|
+
const getAffectedFields = useCallback((changedField) => {
|
|
239
|
+
const affected = /* @__PURE__ */ new Set();
|
|
240
|
+
const toProcess = [changedField];
|
|
241
|
+
while (toProcess.length > 0) {
|
|
242
|
+
const current = toProcess.pop();
|
|
243
|
+
const subscribers = invertedSubscriptions.current.get(current);
|
|
244
|
+
if (subscribers) {
|
|
245
|
+
for (const subscriber of subscribers) {
|
|
246
|
+
if (!affected.has(subscriber)) {
|
|
247
|
+
affected.add(subscriber);
|
|
248
|
+
toProcess.push(subscriber);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return affected;
|
|
254
|
+
}, []);
|
|
255
|
+
const changeField = useCallback(
|
|
256
|
+
(name, value) => {
|
|
257
|
+
if (autoSave) {
|
|
258
|
+
pendingChangedFields.current.add(name);
|
|
259
|
+
const affected = getAffectedFields(name);
|
|
260
|
+
for (const field of affected) {
|
|
261
|
+
pendingAffectedFields.current.add(field);
|
|
262
|
+
}
|
|
263
|
+
debouncedSubmit();
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
[autoSave, getAffectedFields]
|
|
267
|
+
);
|
|
268
|
+
const setFieldValidating = useCallback(
|
|
269
|
+
(name, isValidating) => {
|
|
270
|
+
validatingFields.current.set(name, isValidating);
|
|
271
|
+
},
|
|
272
|
+
[]
|
|
273
|
+
);
|
|
274
|
+
const getFormState = useCallback(() => {
|
|
275
|
+
const values = methods.getValues();
|
|
276
|
+
const formState = methods.formState;
|
|
277
|
+
const fields = {};
|
|
278
|
+
Object.keys(config).forEach((name) => {
|
|
279
|
+
const fieldState = methods.getFieldState(name, formState);
|
|
280
|
+
fields[name] = makeProxyState({
|
|
281
|
+
value: values[name],
|
|
282
|
+
isTouched: fieldState.isTouched,
|
|
283
|
+
isDirty: fieldState.isDirty,
|
|
284
|
+
isValidating: validatingFields.current.get(name) ?? false,
|
|
285
|
+
error: fieldState.error ? {
|
|
286
|
+
type: fieldState.error.type,
|
|
287
|
+
message: fieldState.error.message
|
|
288
|
+
} : void 0,
|
|
289
|
+
invalid: fieldState.invalid
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
return {
|
|
293
|
+
fields,
|
|
294
|
+
record: record ?? {},
|
|
295
|
+
errors: formState.errors,
|
|
296
|
+
defaultValues,
|
|
297
|
+
touchedFields: formState.touchedFields,
|
|
298
|
+
dirtyFields: formState.dirtyFields,
|
|
299
|
+
isDirty: formState.isDirty,
|
|
300
|
+
isTouched: Object.keys(formState.touchedFields).length > 0,
|
|
301
|
+
isValid: formState.isValid,
|
|
302
|
+
isSubmitting: formState.isSubmitting
|
|
303
|
+
};
|
|
304
|
+
}, [methods, config, record, defaultValues]);
|
|
305
|
+
const handleSubmit = useCallback(
|
|
306
|
+
async (values) => {
|
|
307
|
+
for (const [, isValidating] of validatingFields.current) {
|
|
308
|
+
if (isValidating) return;
|
|
309
|
+
}
|
|
310
|
+
if (validate) {
|
|
311
|
+
const errors = await validate(values);
|
|
312
|
+
if (Object.keys(errors).length > 0) {
|
|
313
|
+
Object.entries(errors).forEach(([field, message]) => {
|
|
314
|
+
methods.setError(field, { message });
|
|
315
|
+
});
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const submitValues = transformValuesForSubmit(values, config, mergedInputs);
|
|
320
|
+
await onSubmit?.(submitValues);
|
|
321
|
+
},
|
|
322
|
+
[validate, methods, onSubmit, config, mergedInputs]
|
|
323
|
+
);
|
|
324
|
+
const debouncedSubmitRef = useRef();
|
|
325
|
+
const waitForFieldValidation = useCallback(
|
|
326
|
+
async (fields, version) => {
|
|
327
|
+
const maxWaitMs = 1e4;
|
|
328
|
+
const pollIntervalMs = 50;
|
|
329
|
+
const startTime = Date.now();
|
|
330
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
331
|
+
if (executionVersionRef.current !== version) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
const allDone = fields.every(
|
|
335
|
+
(field) => !validatingFields.current.get(field)
|
|
336
|
+
);
|
|
337
|
+
if (allDone) {
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
341
|
+
}
|
|
342
|
+
return true;
|
|
343
|
+
},
|
|
344
|
+
[]
|
|
345
|
+
);
|
|
346
|
+
const executeAutoSave = useCallback(async () => {
|
|
347
|
+
executionVersionRef.current++;
|
|
348
|
+
const executionVersion = executionVersionRef.current;
|
|
349
|
+
const changedFields = new Set(pendingChangedFields.current);
|
|
350
|
+
const affectedFields = new Set(pendingAffectedFields.current);
|
|
351
|
+
pendingChangedFields.current.clear();
|
|
352
|
+
pendingAffectedFields.current.clear();
|
|
353
|
+
if (changedFields.size === 0) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const fieldsToValidate = [...changedFields, ...affectedFields];
|
|
357
|
+
const validationsComplete = await waitForFieldValidation(
|
|
358
|
+
fieldsToValidate,
|
|
359
|
+
executionVersion
|
|
360
|
+
);
|
|
361
|
+
if (!validationsComplete || executionVersionRef.current !== executionVersion) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (fieldsToValidate.length > 0) {
|
|
365
|
+
const isValid = await methods.trigger(fieldsToValidate);
|
|
366
|
+
if (executionVersionRef.current !== executionVersion) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (!isValid) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const postTriggerComplete = await waitForFieldValidation(
|
|
374
|
+
fieldsToValidate,
|
|
375
|
+
executionVersion
|
|
376
|
+
);
|
|
377
|
+
if (!postTriggerComplete || executionVersionRef.current !== executionVersion) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const formState = methods.formState;
|
|
381
|
+
if (Object.keys(formState.errors).length > 0) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const values = methods.getValues();
|
|
385
|
+
await handleSubmit(values);
|
|
386
|
+
}, [methods, handleSubmit, waitForFieldValidation]);
|
|
387
|
+
useEffect(() => {
|
|
388
|
+
const debouncedFn = debounce(() => {
|
|
389
|
+
executeAutoSave();
|
|
390
|
+
}, debounceMs);
|
|
391
|
+
const fn = Object.assign(debouncedFn, {
|
|
392
|
+
pending: () => false
|
|
393
|
+
// lodash debounce handles this internally
|
|
394
|
+
});
|
|
395
|
+
debouncedSubmitRef.current = fn;
|
|
396
|
+
return () => {
|
|
397
|
+
debouncedFn.cancel();
|
|
398
|
+
};
|
|
399
|
+
}, [executeAutoSave, debounceMs]);
|
|
400
|
+
const debouncedSubmit = useCallback(() => {
|
|
401
|
+
debouncedSubmitRef.current?.();
|
|
402
|
+
}, []);
|
|
403
|
+
const submitImmediate = useCallback(() => {
|
|
404
|
+
debouncedSubmitRef.current?.flush();
|
|
405
|
+
}, []);
|
|
406
|
+
const unusedFields = useMemo(() => {
|
|
407
|
+
const allFields = Object.keys(config);
|
|
408
|
+
return allFields.filter((name) => !registeredFields.has(name));
|
|
409
|
+
}, [config, registeredFields]);
|
|
410
|
+
const resolvedTitle = useMemo(() => {
|
|
411
|
+
if (!formConfig.selectTitle && !formConfig.title) {
|
|
412
|
+
return void 0;
|
|
413
|
+
}
|
|
414
|
+
if (formConfig.selectTitle) {
|
|
415
|
+
const formState = getFormState();
|
|
416
|
+
const context = buildFormContext(
|
|
417
|
+
formState.fields,
|
|
418
|
+
formState.record,
|
|
419
|
+
formState.errors,
|
|
420
|
+
formState.defaultValues,
|
|
421
|
+
formState.touchedFields,
|
|
422
|
+
formState.dirtyFields
|
|
423
|
+
);
|
|
424
|
+
const evaluated = evaluateDescriptor(formConfig.selectTitle, context);
|
|
425
|
+
return resolveFormTitle(formConfig.title, evaluated);
|
|
426
|
+
}
|
|
427
|
+
return resolveFormTitle(formConfig.title);
|
|
428
|
+
}, [formConfig.title, formConfig.selectTitle, getFormState]);
|
|
429
|
+
const contextValue = useMemo(
|
|
430
|
+
() => ({
|
|
431
|
+
config,
|
|
432
|
+
formConfig,
|
|
433
|
+
record,
|
|
434
|
+
registerField,
|
|
435
|
+
unregisterField,
|
|
436
|
+
addSubscription,
|
|
437
|
+
removeSubscription,
|
|
438
|
+
registerWatcherSetter,
|
|
439
|
+
unregisterWatcherSetter,
|
|
440
|
+
changeField,
|
|
441
|
+
setFieldValidating,
|
|
442
|
+
getFormState,
|
|
443
|
+
onSubmit,
|
|
444
|
+
debouncedSubmit: debouncedSubmitRef.current,
|
|
445
|
+
submitImmediate,
|
|
446
|
+
unusedFields,
|
|
447
|
+
methods
|
|
448
|
+
}),
|
|
449
|
+
[
|
|
450
|
+
config,
|
|
451
|
+
formConfig,
|
|
452
|
+
record,
|
|
453
|
+
registerField,
|
|
454
|
+
unregisterField,
|
|
455
|
+
addSubscription,
|
|
456
|
+
removeSubscription,
|
|
457
|
+
registerWatcherSetter,
|
|
458
|
+
unregisterWatcherSetter,
|
|
459
|
+
changeField,
|
|
460
|
+
setFieldValidating,
|
|
461
|
+
getFormState,
|
|
462
|
+
onSubmit,
|
|
463
|
+
submitImmediate,
|
|
464
|
+
unusedFields,
|
|
465
|
+
methods
|
|
466
|
+
]
|
|
467
|
+
);
|
|
468
|
+
const isRenderFunction = typeof children === "function";
|
|
469
|
+
return /* @__PURE__ */ jsx(FormProvider, { ...methods, children: /* @__PURE__ */ jsx(FormContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(GroupContext.Provider, { value: defaultGroupContext2, children: isRenderFunction ? children({
|
|
470
|
+
unusedFields,
|
|
471
|
+
formState: methods.formState,
|
|
472
|
+
methods,
|
|
473
|
+
resolvedTitle
|
|
474
|
+
}) : children }) }) });
|
|
475
|
+
}
|
|
476
|
+
function transformValuesForSubmit(values, config, inputs) {
|
|
477
|
+
const result = {};
|
|
478
|
+
for (const [name, value] of Object.entries(values)) {
|
|
479
|
+
const fieldConfig = config[name];
|
|
480
|
+
const type = fieldConfig?.type ?? "textField";
|
|
481
|
+
const inputConfig = inputs[type];
|
|
482
|
+
if (inputConfig) {
|
|
483
|
+
const submitName = transformFieldName(name, inputConfig.getSubmitField);
|
|
484
|
+
const submitValue = extractValueField(value, inputConfig.valueField);
|
|
485
|
+
result[submitName] = submitValue;
|
|
486
|
+
} else {
|
|
487
|
+
result[name] = value;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return result;
|
|
491
|
+
}
|
|
492
|
+
function useInferredInputs(options) {
|
|
493
|
+
const { selectProps, conditions = [], subscribesTo = [] } = options;
|
|
494
|
+
return useMemo(() => {
|
|
495
|
+
const inferred = [...subscribesTo];
|
|
496
|
+
if (selectProps) {
|
|
497
|
+
inferred.push(...inferFieldsFromDescriptor(selectProps));
|
|
498
|
+
}
|
|
499
|
+
if (conditions.length > 0) {
|
|
500
|
+
inferred.push(...inferFieldsFromConditions(conditions));
|
|
501
|
+
}
|
|
502
|
+
return [...new Set(inferred)];
|
|
503
|
+
}, [selectProps, conditions, subscribesTo]);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/hooks/useConditions.ts
|
|
507
|
+
function useConditions(options) {
|
|
508
|
+
const { conditions, subscribesTo, props } = options;
|
|
509
|
+
const { record, methods } = useFormContext();
|
|
510
|
+
const watchFields = useInferredInputs({
|
|
511
|
+
conditions,
|
|
512
|
+
subscribesTo
|
|
513
|
+
});
|
|
514
|
+
const watchedValues = useWatch({
|
|
515
|
+
control: methods.control,
|
|
516
|
+
name: watchFields.length > 0 ? watchFields : []
|
|
517
|
+
});
|
|
518
|
+
const fieldValues = useMemo(() => {
|
|
519
|
+
const values = {};
|
|
520
|
+
if (watchFields.length === 0) {
|
|
521
|
+
return values;
|
|
522
|
+
}
|
|
523
|
+
if (Array.isArray(watchedValues)) {
|
|
524
|
+
watchFields.forEach((field, i) => {
|
|
525
|
+
values[field] = watchedValues[i];
|
|
526
|
+
});
|
|
527
|
+
} else {
|
|
528
|
+
values[watchFields[0]] = watchedValues;
|
|
529
|
+
}
|
|
530
|
+
return values;
|
|
531
|
+
}, [watchFields, watchedValues]);
|
|
532
|
+
const fieldStates = useMemo(() => {
|
|
533
|
+
const states = {};
|
|
534
|
+
if (watchFields.length === 0) {
|
|
535
|
+
return states;
|
|
536
|
+
}
|
|
537
|
+
watchFields.forEach((fieldName) => {
|
|
538
|
+
const fieldState = methods.getFieldState(fieldName);
|
|
539
|
+
states[fieldName] = {
|
|
540
|
+
value: fieldValues[fieldName],
|
|
541
|
+
isTouched: fieldState.isTouched,
|
|
542
|
+
isDirty: fieldState.isDirty,
|
|
543
|
+
error: fieldState.error,
|
|
544
|
+
invalid: fieldState.invalid,
|
|
545
|
+
isValidating: false
|
|
546
|
+
// Not easily available per-field
|
|
547
|
+
};
|
|
548
|
+
});
|
|
549
|
+
return states;
|
|
550
|
+
}, [watchFields, fieldValues, methods]);
|
|
551
|
+
return useMemo(() => {
|
|
552
|
+
if (conditions.length === 0) {
|
|
553
|
+
return {
|
|
554
|
+
disabled: void 0,
|
|
555
|
+
visible: void 0,
|
|
556
|
+
setValue: void 0,
|
|
557
|
+
hasDisabledCondition: false,
|
|
558
|
+
hasVisibleCondition: false,
|
|
559
|
+
hasSetCondition: false
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
return evaluateConditions({
|
|
563
|
+
conditions,
|
|
564
|
+
fieldValues,
|
|
565
|
+
fieldStates,
|
|
566
|
+
record,
|
|
567
|
+
props
|
|
568
|
+
});
|
|
569
|
+
}, [conditions, fieldValues, fieldStates, record, props]);
|
|
570
|
+
}
|
|
571
|
+
function usePropsEvaluation(options) {
|
|
572
|
+
const { selectProps, subscribesTo, fieldName } = options;
|
|
573
|
+
const { record, methods } = useFormContext();
|
|
574
|
+
const watchFields = useInferredInputs({
|
|
575
|
+
selectProps,
|
|
576
|
+
subscribesTo
|
|
577
|
+
});
|
|
578
|
+
const watchedValues = useWatch({
|
|
579
|
+
control: methods.control,
|
|
580
|
+
name: watchFields.length > 0 ? watchFields : []
|
|
581
|
+
});
|
|
582
|
+
const formState = useMemo(() => {
|
|
583
|
+
const fields = {};
|
|
584
|
+
if (watchFields.length > 0) {
|
|
585
|
+
const values = watchFields.length === 1 ? { [watchFields[0]]: watchedValues } : watchFields.reduce((acc, field, i) => {
|
|
586
|
+
acc[field] = watchedValues[i];
|
|
587
|
+
return acc;
|
|
588
|
+
}, {});
|
|
589
|
+
watchFields.forEach((name) => {
|
|
590
|
+
fields[name] = makeProxyState({
|
|
591
|
+
value: values[name],
|
|
592
|
+
isTouched: false,
|
|
593
|
+
isDirty: false,
|
|
594
|
+
isValidating: false,
|
|
595
|
+
error: void 0,
|
|
596
|
+
invalid: false
|
|
597
|
+
});
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
return {
|
|
601
|
+
fields,
|
|
602
|
+
record: record ?? {},
|
|
603
|
+
errors: {},
|
|
604
|
+
defaultValues: {},
|
|
605
|
+
touchedFields: {},
|
|
606
|
+
dirtyFields: {},
|
|
607
|
+
isDirty: false,
|
|
608
|
+
isTouched: false,
|
|
609
|
+
isValid: true,
|
|
610
|
+
isSubmitting: false
|
|
611
|
+
};
|
|
612
|
+
}, [watchFields, watchedValues, record]);
|
|
613
|
+
return useMemo(() => {
|
|
614
|
+
if (!selectProps) {
|
|
615
|
+
return {};
|
|
616
|
+
}
|
|
617
|
+
if (typeof selectProps === "function") {
|
|
618
|
+
const result2 = selectProps(formState, methods);
|
|
619
|
+
return result2 ?? {};
|
|
620
|
+
}
|
|
621
|
+
const context = buildFieldContext(formState, fieldName);
|
|
622
|
+
const result = evaluateDescriptor(selectProps, context);
|
|
623
|
+
return result ?? {};
|
|
624
|
+
}, [selectProps, formState, methods, fieldName]);
|
|
625
|
+
}
|
|
626
|
+
function useSubscriptions(fieldName, subscriptions) {
|
|
627
|
+
const { addSubscription, removeSubscription } = useFormContext();
|
|
628
|
+
const prevSubscriptionsRef = useRef([]);
|
|
629
|
+
useEffect(() => {
|
|
630
|
+
const prevSubscriptions = prevSubscriptionsRef.current;
|
|
631
|
+
const toRemove = prevSubscriptions.filter(
|
|
632
|
+
(target) => !subscriptions.includes(target)
|
|
633
|
+
);
|
|
634
|
+
const toAdd = subscriptions.filter(
|
|
635
|
+
(target) => !prevSubscriptions.includes(target)
|
|
636
|
+
);
|
|
637
|
+
toRemove.forEach((target) => {
|
|
638
|
+
removeSubscription(target, fieldName);
|
|
639
|
+
});
|
|
640
|
+
toAdd.forEach((target) => {
|
|
641
|
+
addSubscription(target, fieldName);
|
|
642
|
+
});
|
|
643
|
+
prevSubscriptionsRef.current = subscriptions;
|
|
644
|
+
return () => {
|
|
645
|
+
subscriptions.forEach((target) => {
|
|
646
|
+
removeSubscription(target, fieldName);
|
|
647
|
+
});
|
|
648
|
+
};
|
|
649
|
+
}, [fieldName, subscriptions, addSubscription, removeSubscription]);
|
|
650
|
+
}
|
|
651
|
+
function Field({
|
|
652
|
+
name,
|
|
653
|
+
type: typeProp,
|
|
654
|
+
disabled: disabledProp,
|
|
655
|
+
hidden: hiddenProp,
|
|
656
|
+
children,
|
|
657
|
+
shouldRegister = true,
|
|
658
|
+
...restProps
|
|
659
|
+
}) {
|
|
660
|
+
const {
|
|
661
|
+
config,
|
|
662
|
+
formConfig,
|
|
663
|
+
methods,
|
|
664
|
+
registerField,
|
|
665
|
+
unregisterField,
|
|
666
|
+
registerWatcherSetter,
|
|
667
|
+
unregisterWatcherSetter,
|
|
668
|
+
changeField,
|
|
669
|
+
setFieldValidating
|
|
670
|
+
} = useFormContext();
|
|
671
|
+
const providerConfig = useConfigContext();
|
|
672
|
+
const groupContext = useGroupContext();
|
|
673
|
+
const fieldConfig = config[name] ?? {};
|
|
674
|
+
const type = typeProp ?? fieldConfig.type ?? "textField";
|
|
675
|
+
const inputConfig = useMemo(() => {
|
|
676
|
+
const formInputs = typeof formConfig.inputs === "function" ? formConfig.inputs(providerConfig.inputs) : formConfig.inputs ?? {};
|
|
677
|
+
const mergedInputs = { ...providerConfig.inputs };
|
|
678
|
+
for (const [key, override] of Object.entries(formInputs)) {
|
|
679
|
+
if (mergedInputs[key]) {
|
|
680
|
+
mergedInputs[key] = { ...mergedInputs[key], ...override };
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return resolveInputConfig(type, mergedInputs) ?? { component: "input", defaultValue: "" };
|
|
684
|
+
}, [type, providerConfig.inputs, formConfig.inputs]);
|
|
685
|
+
useEffect(() => {
|
|
686
|
+
if (shouldRegister) {
|
|
687
|
+
registerField(name);
|
|
688
|
+
return () => unregisterField(name);
|
|
689
|
+
}
|
|
690
|
+
}, [name, shouldRegister, registerField, unregisterField]);
|
|
691
|
+
const [watchers, setWatchers] = useState({});
|
|
692
|
+
useEffect(() => {
|
|
693
|
+
registerWatcherSetter(name, setWatchers);
|
|
694
|
+
return () => unregisterWatcherSetter(name);
|
|
695
|
+
}, [name, registerWatcherSetter, unregisterWatcherSetter]);
|
|
696
|
+
const inferredSubscriptions = useInferredInputs({
|
|
697
|
+
selectProps: fieldConfig.selectProps,
|
|
698
|
+
conditions: fieldConfig.conditions,
|
|
699
|
+
subscribesTo: fieldConfig.subscribesTo
|
|
700
|
+
});
|
|
701
|
+
const allSubscriptions = useMemo(() => {
|
|
702
|
+
return [.../* @__PURE__ */ new Set([...inferredSubscriptions, ...groupContext.subscriptions])];
|
|
703
|
+
}, [inferredSubscriptions, groupContext.subscriptions]);
|
|
704
|
+
useSubscriptions(name, allSubscriptions);
|
|
705
|
+
const conditionResult = useConditions({
|
|
706
|
+
conditions: fieldConfig.conditions ?? [],
|
|
707
|
+
subscribesTo: fieldConfig.subscribesTo,
|
|
708
|
+
props: { name }
|
|
709
|
+
});
|
|
710
|
+
const setValueRef = useRef(methods.setValue);
|
|
711
|
+
setValueRef.current = methods.setValue;
|
|
712
|
+
const getValuesRef = useRef(methods.getValues);
|
|
713
|
+
getValuesRef.current = methods.getValues;
|
|
714
|
+
const effectiveSetValue = useMemo(() => {
|
|
715
|
+
if (conditionResult.hasSetCondition) {
|
|
716
|
+
return { hasCondition: true, value: conditionResult.setValue };
|
|
717
|
+
}
|
|
718
|
+
if (groupContext.state.hasSetCondition) {
|
|
719
|
+
return { hasCondition: true, value: groupContext.state.setValue };
|
|
720
|
+
}
|
|
721
|
+
return { hasCondition: false, value: void 0 };
|
|
722
|
+
}, [
|
|
723
|
+
conditionResult.hasSetCondition,
|
|
724
|
+
conditionResult.setValue,
|
|
725
|
+
groupContext.state.hasSetCondition,
|
|
726
|
+
groupContext.state.setValue
|
|
727
|
+
]);
|
|
728
|
+
useEffect(() => {
|
|
729
|
+
if (effectiveSetValue.hasCondition && effectiveSetValue.value !== void 0) {
|
|
730
|
+
const currentValue = getValuesRef.current(name);
|
|
731
|
+
if (currentValue !== effectiveSetValue.value) {
|
|
732
|
+
setValueRef.current(name, effectiveSetValue.value, {
|
|
733
|
+
shouldValidate: true,
|
|
734
|
+
shouldDirty: true,
|
|
735
|
+
shouldTouch: false
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}, [effectiveSetValue.hasCondition, effectiveSetValue.value, name]);
|
|
740
|
+
const isDisabled = useMemo(() => {
|
|
741
|
+
if (disabledProp !== void 0) return disabledProp;
|
|
742
|
+
if (fieldConfig.disabled !== void 0) return fieldConfig.disabled;
|
|
743
|
+
if (conditionResult.hasDisabledCondition)
|
|
744
|
+
return conditionResult.disabled ?? false;
|
|
745
|
+
if (groupContext.state.isDisabled) return true;
|
|
746
|
+
return false;
|
|
747
|
+
}, [
|
|
748
|
+
disabledProp,
|
|
749
|
+
fieldConfig.disabled,
|
|
750
|
+
conditionResult,
|
|
751
|
+
groupContext.state.isDisabled
|
|
752
|
+
]);
|
|
753
|
+
const isVisible = useMemo(() => {
|
|
754
|
+
if (hiddenProp !== void 0) return !hiddenProp;
|
|
755
|
+
if (fieldConfig.hidden !== void 0) return !fieldConfig.hidden;
|
|
756
|
+
if (conditionResult.hasVisibleCondition)
|
|
757
|
+
return conditionResult.visible ?? true;
|
|
758
|
+
if (!groupContext.state.isVisible) return false;
|
|
759
|
+
return true;
|
|
760
|
+
}, [hiddenProp, fieldConfig.hidden, conditionResult, groupContext.state.isVisible]);
|
|
761
|
+
const evaluatedSelectProps = usePropsEvaluation({
|
|
762
|
+
selectProps: fieldConfig.selectProps,
|
|
763
|
+
subscribesTo: fieldConfig.subscribesTo,
|
|
764
|
+
fieldName: name
|
|
765
|
+
});
|
|
766
|
+
const label = useMemo(() => {
|
|
767
|
+
return resolveLabel(name, fieldConfig, evaluatedSelectProps, restProps);
|
|
768
|
+
}, [name, fieldConfig, evaluatedSelectProps, restProps]);
|
|
769
|
+
const validationRules = useMemo(() => {
|
|
770
|
+
return {
|
|
771
|
+
...fieldConfig.rules,
|
|
772
|
+
validate: async (value) => {
|
|
773
|
+
setFieldValidating(name, true);
|
|
774
|
+
try {
|
|
775
|
+
if (fieldConfig.validator) {
|
|
776
|
+
const result = await runValidator(
|
|
777
|
+
fieldConfig.validator,
|
|
778
|
+
value,
|
|
779
|
+
methods.getValues(),
|
|
780
|
+
providerConfig.validators
|
|
781
|
+
);
|
|
782
|
+
if (result !== true && result !== void 0) {
|
|
783
|
+
return resolveErrorMessage(result, providerConfig.errorMessages);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
if (inputConfig.validator) {
|
|
787
|
+
const result = await runValidator(
|
|
788
|
+
inputConfig.validator,
|
|
789
|
+
value,
|
|
790
|
+
methods.getValues(),
|
|
791
|
+
providerConfig.validators
|
|
792
|
+
);
|
|
793
|
+
if (result !== true && result !== void 0) {
|
|
794
|
+
return resolveErrorMessage(result, providerConfig.errorMessages);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return true;
|
|
798
|
+
} finally {
|
|
799
|
+
setFieldValidating(name, false);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
}, [
|
|
804
|
+
fieldConfig.rules,
|
|
805
|
+
fieldConfig.validator,
|
|
806
|
+
inputConfig.validator,
|
|
807
|
+
methods,
|
|
808
|
+
providerConfig.validators,
|
|
809
|
+
providerConfig.errorMessages,
|
|
810
|
+
name,
|
|
811
|
+
setFieldValidating
|
|
812
|
+
]);
|
|
813
|
+
const handleChange = useCallback(
|
|
814
|
+
(onChange) => (newValue) => {
|
|
815
|
+
const parsedValue = parse(
|
|
816
|
+
newValue,
|
|
817
|
+
inputConfig.parser,
|
|
818
|
+
providerConfig.parsers
|
|
819
|
+
);
|
|
820
|
+
onChange(parsedValue);
|
|
821
|
+
changeField(name, parsedValue);
|
|
822
|
+
},
|
|
823
|
+
[inputConfig.parser, providerConfig.parsers, changeField, name]
|
|
824
|
+
);
|
|
825
|
+
if (!isVisible) {
|
|
826
|
+
return null;
|
|
827
|
+
}
|
|
828
|
+
return /* @__PURE__ */ jsx(
|
|
829
|
+
Controller,
|
|
830
|
+
{
|
|
831
|
+
control: methods.control,
|
|
832
|
+
name,
|
|
833
|
+
rules: validationRules,
|
|
834
|
+
render: ({ field, fieldState, formState }) => {
|
|
835
|
+
const formattedValue = format(
|
|
836
|
+
field.value,
|
|
837
|
+
inputConfig.formatter,
|
|
838
|
+
providerConfig.formatters
|
|
839
|
+
);
|
|
840
|
+
const finalProps = mergeFieldProps({
|
|
841
|
+
providerDefaultFieldProps: providerConfig.defaultFieldProps,
|
|
842
|
+
providerSelectDefaultFieldProps: {},
|
|
843
|
+
// Evaluated at provider level if needed
|
|
844
|
+
formDefaultFieldProps: formConfig.defaultFieldProps,
|
|
845
|
+
formSelectDefaultFieldProps: {},
|
|
846
|
+
// Evaluated at form level if needed
|
|
847
|
+
inputProps: inputConfig.props,
|
|
848
|
+
fieldConfigProps: fieldConfig.props,
|
|
849
|
+
selectProps: evaluatedSelectProps,
|
|
850
|
+
componentProps: restProps,
|
|
851
|
+
coreProps: {
|
|
852
|
+
name,
|
|
853
|
+
label,
|
|
854
|
+
disabled: isDisabled,
|
|
855
|
+
error: fieldState.error?.message,
|
|
856
|
+
[inputConfig.inputFieldProp ?? "value"]: formattedValue,
|
|
857
|
+
onChange: handleChange(field.onChange),
|
|
858
|
+
onBlur: field.onBlur,
|
|
859
|
+
ref: field.ref
|
|
860
|
+
}
|
|
861
|
+
});
|
|
862
|
+
const Component = inputConfig.component;
|
|
863
|
+
const template = inputConfig.template ?? providerConfig.inputTemplates[type] ?? providerConfig.defaultInputTemplate;
|
|
864
|
+
const TemplateComponent = template;
|
|
865
|
+
const renderedField = TemplateComponent ? /* @__PURE__ */ jsx(
|
|
866
|
+
TemplateComponent,
|
|
867
|
+
{
|
|
868
|
+
Field: Component,
|
|
869
|
+
fieldProps: finalProps,
|
|
870
|
+
fieldState,
|
|
871
|
+
formState
|
|
872
|
+
}
|
|
873
|
+
) : /* @__PURE__ */ jsx(Component, { ...finalProps });
|
|
874
|
+
if (typeof children === "function") {
|
|
875
|
+
const result = children({
|
|
876
|
+
fieldState,
|
|
877
|
+
renderedField,
|
|
878
|
+
fieldProps: finalProps,
|
|
879
|
+
watchers,
|
|
880
|
+
formState
|
|
881
|
+
});
|
|
882
|
+
return /* @__PURE__ */ jsx(Fragment, { children: result });
|
|
883
|
+
}
|
|
884
|
+
return renderedField;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
function FieldGroup({ name, children }) {
|
|
890
|
+
const { formConfig } = useFormContext();
|
|
891
|
+
const parentContext = useGroupContext();
|
|
892
|
+
const groupConfig = formConfig.groups?.[name] ?? {
|
|
893
|
+
conditions: [],
|
|
894
|
+
subscribesTo: []
|
|
895
|
+
};
|
|
896
|
+
if (process.env.NODE_ENV !== "production" && !formConfig.groups?.[name]) {
|
|
897
|
+
console.warn(
|
|
898
|
+
`FieldGroup: No config found for group "${name}". Make sure to define it in formConfig.groups.`
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
const inferredInputs = useInferredInputs({
|
|
902
|
+
conditions: groupConfig.conditions,
|
|
903
|
+
subscribesTo: groupConfig.subscribesTo
|
|
904
|
+
});
|
|
905
|
+
const conditionResult = useConditions({
|
|
906
|
+
conditions: groupConfig.conditions ?? [],
|
|
907
|
+
subscribesTo: groupConfig.subscribesTo
|
|
908
|
+
});
|
|
909
|
+
const mergedState = useMemo(() => {
|
|
910
|
+
const isDisabled = (conditionResult.hasDisabledCondition ? conditionResult.disabled ?? false : false) || parentContext.state.isDisabled;
|
|
911
|
+
const isVisible = (conditionResult.hasVisibleCondition ? conditionResult.visible ?? true : true) && parentContext.state.isVisible;
|
|
912
|
+
const hasSetCondition = conditionResult.hasSetCondition || parentContext.state.hasSetCondition;
|
|
913
|
+
const setValue = conditionResult.hasSetCondition ? conditionResult.setValue : parentContext.state.setValue;
|
|
914
|
+
const conditions = [
|
|
915
|
+
...parentContext.state.conditions,
|
|
916
|
+
...groupConfig.conditions ?? []
|
|
917
|
+
];
|
|
918
|
+
const subscriptions = [
|
|
919
|
+
...parentContext.state.subscriptions,
|
|
920
|
+
...groupConfig.subscribesTo ?? []
|
|
921
|
+
];
|
|
922
|
+
return {
|
|
923
|
+
isDisabled,
|
|
924
|
+
isVisible,
|
|
925
|
+
hasSetCondition,
|
|
926
|
+
setValue,
|
|
927
|
+
conditions,
|
|
928
|
+
subscriptions
|
|
929
|
+
};
|
|
930
|
+
}, [conditionResult, parentContext.state, groupConfig]);
|
|
931
|
+
const contextValue = useMemo(
|
|
932
|
+
() => ({
|
|
933
|
+
state: mergedState,
|
|
934
|
+
subscriptions: mergedState.subscriptions,
|
|
935
|
+
inferredInputs,
|
|
936
|
+
config: groupConfig
|
|
937
|
+
}),
|
|
938
|
+
[mergedState, inferredInputs, groupConfig]
|
|
939
|
+
);
|
|
940
|
+
return /* @__PURE__ */ jsx(GroupContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
|
|
941
|
+
"span",
|
|
942
|
+
{
|
|
943
|
+
style: { display: mergedState.isVisible ? void 0 : "none" },
|
|
944
|
+
"data-formality-group": name,
|
|
945
|
+
children
|
|
946
|
+
}
|
|
947
|
+
) });
|
|
948
|
+
}
|
|
949
|
+
function UnusedFields({ children }) {
|
|
950
|
+
const { unusedFields, config } = useFormContext();
|
|
951
|
+
const sortedFields = useMemo(() => {
|
|
952
|
+
return sortFieldsByOrder(unusedFields, config);
|
|
953
|
+
}, [unusedFields, config]);
|
|
954
|
+
if (children) {
|
|
955
|
+
return /* @__PURE__ */ jsx(Fragment, { children: sortedFields.map(
|
|
956
|
+
(name) => children({
|
|
957
|
+
name,
|
|
958
|
+
// CRITICAL: shouldRegister={false} prevents infinite loop (PRD 5.5, 18.9)
|
|
959
|
+
component: /* @__PURE__ */ jsx(Field, { name, shouldRegister: false }, name)
|
|
960
|
+
})
|
|
961
|
+
) });
|
|
962
|
+
}
|
|
963
|
+
return /* @__PURE__ */ jsx(Fragment, { children: sortedFields.map((name) => (
|
|
964
|
+
// CRITICAL: shouldRegister={false} prevents infinite loop (PRD 5.5, 18.9)
|
|
965
|
+
/* @__PURE__ */ jsx(Field, { name, shouldRegister: false }, name)
|
|
966
|
+
)) });
|
|
967
|
+
}
|
|
968
|
+
function useFormState(options) {
|
|
969
|
+
const rhfContext = useFormContext$1();
|
|
970
|
+
let formalityContext = null;
|
|
971
|
+
try {
|
|
972
|
+
formalityContext = useFormContext();
|
|
973
|
+
} catch {
|
|
974
|
+
}
|
|
975
|
+
const fieldNames = useMemo(() => {
|
|
976
|
+
return Array.isArray(options.name) ? options.name : [options.name];
|
|
977
|
+
}, [options.name]);
|
|
978
|
+
const watchedValues = useWatch({
|
|
979
|
+
control: rhfContext.control,
|
|
980
|
+
name: fieldNames
|
|
981
|
+
});
|
|
982
|
+
const fields = useMemo(() => {
|
|
983
|
+
const result2 = {};
|
|
984
|
+
if (fieldNames.length === 0) {
|
|
985
|
+
return result2;
|
|
986
|
+
}
|
|
987
|
+
const values = fieldNames.length === 1 ? [watchedValues] : watchedValues;
|
|
988
|
+
fieldNames.forEach((name, index) => {
|
|
989
|
+
const value = values[index];
|
|
990
|
+
result2[name] = makeProxyState({
|
|
991
|
+
value,
|
|
992
|
+
isTouched: false,
|
|
993
|
+
isDirty: false,
|
|
994
|
+
isValidating: false,
|
|
995
|
+
error: void 0,
|
|
996
|
+
invalid: false
|
|
997
|
+
});
|
|
998
|
+
});
|
|
999
|
+
return result2;
|
|
1000
|
+
}, [fieldNames, watchedValues]);
|
|
1001
|
+
const result = useMemo(() => {
|
|
1002
|
+
const base = {
|
|
1003
|
+
fields,
|
|
1004
|
+
record: {},
|
|
1005
|
+
// Will be overwritten by defineProperty
|
|
1006
|
+
// Provide minimal form-level state that doesn't require subscriptions
|
|
1007
|
+
isDirty: false,
|
|
1008
|
+
isTouched: false,
|
|
1009
|
+
isValid: true,
|
|
1010
|
+
isSubmitting: false,
|
|
1011
|
+
errors: {},
|
|
1012
|
+
touchedFields: {},
|
|
1013
|
+
dirtyFields: {},
|
|
1014
|
+
defaultValues: {}
|
|
1015
|
+
};
|
|
1016
|
+
Object.defineProperty(base, "record", {
|
|
1017
|
+
get: () => formalityContext?.record ?? {},
|
|
1018
|
+
enumerable: true,
|
|
1019
|
+
configurable: true
|
|
1020
|
+
});
|
|
1021
|
+
return base;
|
|
1022
|
+
}, [fields, formalityContext?.record]);
|
|
1023
|
+
return result;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
export { ConfigContext, Field, FieldGroup, Form, FormContext, FormalityProvider, GroupContext, UnusedFields, makeDeepProxyState, makeProxyState, useConditions, useConfigContext, useFormContext, useFormState, useGroupContext, useInferredInputs, usePropsEvaluation, useSubscriptions };
|
|
1027
|
+
//# sourceMappingURL=index.js.map
|
|
1028
|
+
//# sourceMappingURL=index.js.map
|