@formality-ui/react 0.3.0 → 0.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/index.d.cts CHANGED
@@ -133,6 +133,15 @@ interface ReactInputConfig<TValue = unknown> extends Omit<InputConfig<TValue>, "
133
133
  *
134
134
  * The generic `V` defaults to `FieldValues`; pass your form's values type for
135
135
  * slightly tighter checking on path-based rules.
136
+ *
137
+ * The §6.4 field-level override fields (`defaultValue`, `debounce`,
138
+ * `parser`, `formatter`, `valueField`, `getSubmitField`) are inherited
139
+ * from core `FieldConfig` unchanged. They override their `InputConfig`
140
+ * counterparts when `!== undefined`, using the single
141
+ * `resolveFieldOverType` rule (§6.4.0). The field-level
142
+ * `parser`/`formatter` stay `string | ((value: unknown) => unknown)` —
143
+ * NOT generified over `TValue` (per-field `TValue` is a future
144
+ * enhancement, §3.2.1).
136
145
  */
137
146
  interface ReactFieldConfig<V extends FieldValues = FieldValues> extends Omit<FieldConfig, "rules"> {
138
147
  /** react-hook-form register options forwarded to the field's Controller. */
package/dist/index.d.ts CHANGED
@@ -133,6 +133,15 @@ interface ReactInputConfig<TValue = unknown> extends Omit<InputConfig<TValue>, "
133
133
  *
134
134
  * The generic `V` defaults to `FieldValues`; pass your form's values type for
135
135
  * slightly tighter checking on path-based rules.
136
+ *
137
+ * The §6.4 field-level override fields (`defaultValue`, `debounce`,
138
+ * `parser`, `formatter`, `valueField`, `getSubmitField`) are inherited
139
+ * from core `FieldConfig` unchanged. They override their `InputConfig`
140
+ * counterparts when `!== undefined`, using the single
141
+ * `resolveFieldOverType` rule (§6.4.0). The field-level
142
+ * `parser`/`formatter` stay `string | ((value: unknown) => unknown)` —
143
+ * NOT generified over `TValue` (per-field `TValue` is a future
144
+ * enhancement, §3.2.1).
136
145
  */
137
146
  interface ReactFieldConfig<V extends FieldValues = FieldValues> extends Omit<FieldConfig, "rules"> {
138
147
  /** react-hook-form register options forwarded to the field's Controller. */
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { createContext, useContext, useMemo, useRef, useState, useCallback, useE
2
2
  import { jsx, Fragment } from 'react/jsx-runtime';
3
3
  import { useForm, FormProvider, useWatch, Controller, useFormContext as useFormContext$1 } from 'react-hook-form';
4
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';
5
+ import { resolveAllInitialValues, resolveFieldOverType, buildFormContext, evaluateDescriptor, resolveFormTitle, transformFieldName, extractValueField, inferFieldsFromDescriptor, inferFieldsFromConditions, evaluateConditions, buildFieldContext, resolveInputConfig, resolveLabel, runValidator, resolveErrorMessage, parse, format, mergeFieldProps, sortFieldsByOrder } from '@formality-ui/core';
6
6
 
7
7
  // src/context/ConfigContext.ts
8
8
  var defaultConfigContext = {
@@ -162,8 +162,15 @@ function Form({
162
162
  return result;
163
163
  }, [providerConfig.inputs, formConfig.inputs]);
164
164
  const defaultValues = useMemo(() => {
165
- const resolved = resolveAllInitialValues(config, mergedInputs, record ?? {});
166
- const baseline = { ...record ?? {}, ...resolved };
165
+ const resolved = resolveAllInitialValues(
166
+ config,
167
+ mergedInputs,
168
+ record ?? {}
169
+ );
170
+ const baseline = {
171
+ ...record ?? {},
172
+ ...resolved
173
+ };
167
174
  for (const fieldName of Object.keys(config)) {
168
175
  if (!(fieldName in baseline)) baseline[fieldName] = void 0;
169
176
  }
@@ -201,11 +208,6 @@ function Form({
201
208
  invertedSubscriptions.current.set(target, /* @__PURE__ */ new Set());
202
209
  }
203
210
  invertedSubscriptions.current.get(target).add(subscriber);
204
- if (process.env.NODE_ENV !== "production") {
205
- console.warn(
206
- `[Formality Subscription] "${subscriber}" added to watch "${target}"`
207
- );
208
- }
209
211
  const setter = watcherSetters.current.get(target);
210
212
  if (setter) {
211
213
  setter((prev) => ({ ...prev, [subscriber]: true }));
@@ -218,20 +220,7 @@ function Form({
218
220
  }, []);
219
221
  const removeSubscription = useCallback(
220
222
  (target, subscriber) => {
221
- const subscribers = invertedSubscriptions.current.get(target);
222
- const subscriptionExists = subscribers?.has(subscriber) ?? false;
223
223
  invertedSubscriptions.current.get(target)?.delete(subscriber);
224
- if (process.env.NODE_ENV !== "production") {
225
- if (subscriptionExists) {
226
- console.warn(
227
- `[Formality Subscription] "${subscriber}" removed from watching "${target}"`
228
- );
229
- } else {
230
- console.warn(
231
- `[Formality Subscription] WARNING: Double-cleanup attempt - "${subscriber}" was not watching "${target}"`
232
- );
233
- }
234
- }
235
224
  const setter = watcherSetters.current.get(target);
236
225
  if (setter) {
237
226
  setter((prev) => {
@@ -288,7 +277,11 @@ function Form({
288
277
  for (const field of affected) {
289
278
  pendingAffectedFields.current.add(field);
290
279
  }
291
- const fieldDebounce = inputConfig?.debounce;
280
+ const fieldConfig = config[name];
281
+ const fieldDebounce = resolveFieldOverType(
282
+ fieldConfig?.debounce,
283
+ inputConfig?.debounce
284
+ );
292
285
  if (fieldDebounce === false) {
293
286
  executeAutoSaveRef.current?.();
294
287
  } else if (typeof fieldDebounce === "number") {
@@ -298,7 +291,7 @@ function Form({
298
291
  }
299
292
  }
300
293
  },
301
- [autoSave, getAffectedFields]
294
+ [autoSave, getAffectedFields, config]
302
295
  );
303
296
  const setFieldValidating = useCallback(
304
297
  (name, isValidating) => {
@@ -602,13 +595,17 @@ function transformValuesForSubmit(values, config, inputs) {
602
595
  const fieldConfig = config[name];
603
596
  const type = fieldConfig?.type ?? "textField";
604
597
  const inputConfig = inputs[type];
605
- if (inputConfig) {
606
- const submitName = transformFieldName(name, inputConfig.getSubmitField);
607
- const submitValue = extractValueField(value, inputConfig.valueField);
608
- result[submitName] = submitValue;
609
- } else {
610
- result[name] = value;
611
- }
598
+ const effectiveGetSubmitField = resolveFieldOverType(
599
+ fieldConfig?.getSubmitField,
600
+ inputConfig?.getSubmitField
601
+ );
602
+ const effectiveValueField = resolveFieldOverType(
603
+ fieldConfig?.valueField,
604
+ inputConfig?.valueField
605
+ );
606
+ const submitName = transformFieldName(name, effectiveGetSubmitField);
607
+ const submitValue = extractValueField(value, effectiveValueField);
608
+ result[submitName] = submitValue;
612
609
  }
613
610
  return result;
614
611
  }
@@ -918,20 +915,10 @@ function useSubscriptions(fieldName, subscriptions) {
918
915
  runSubscriptionsRef.current.set(currentRunId, [...subscriptions]);
919
916
  subscriptions.forEach((target) => {
920
917
  addSubscription(target, fieldName);
921
- if (process.env.NODE_ENV !== "production") {
922
- console.warn(
923
- `[Formality Subscription] Run ${currentRunId}: "${fieldName}" subscribing to "${target}"`
924
- );
925
- }
926
918
  });
927
919
  return () => {
928
920
  const thisRunSubscriptions = runSubscriptionsRef.current.get(currentRunId);
929
921
  if (thisRunSubscriptions) {
930
- if (process.env.NODE_ENV !== "production") {
931
- console.warn(
932
- `[Formality Subscription] Run ${currentRunId}: "${fieldName}" cleaning up [${thisRunSubscriptions.join(", ")}]`
933
- );
934
- }
935
922
  [...thisRunSubscriptions].reverse().forEach((target) => {
936
923
  removeSubscription(target, fieldName);
937
924
  });
@@ -1095,8 +1082,22 @@ function useField({
1095
1082
  groupContext.state.isVisible
1096
1083
  ]);
1097
1084
  const label = useMemo(() => {
1098
- return resolveLabel(name, fieldConfig, fieldSelectProps, restProps);
1099
- }, [name, fieldConfig, fieldSelectProps, restProps]);
1085
+ return resolveLabel(
1086
+ name,
1087
+ fieldConfig,
1088
+ fieldSelectProps,
1089
+ restProps,
1090
+ providerSelectProps,
1091
+ formSelectProps
1092
+ );
1093
+ }, [
1094
+ name,
1095
+ fieldConfig,
1096
+ fieldSelectProps,
1097
+ restProps,
1098
+ providerSelectProps,
1099
+ formSelectProps
1100
+ ]);
1100
1101
  const passSubscriptionsEnabled = fieldConfig.passSubscriptions === true;
1101
1102
  const provideStateEnabled = fieldConfig.provideState === true;
1102
1103
  const subscribedWatchNames = useMemo(
@@ -1175,23 +1176,25 @@ function useField({
1175
1176
  name,
1176
1177
  setFieldValidating
1177
1178
  ]);
1179
+ const effectiveParser = useMemo(
1180
+ () => resolveFieldOverType(fieldConfig.parser, inputConfig.parser),
1181
+ [fieldConfig.parser, inputConfig.parser]
1182
+ );
1183
+ const effectiveFormatter = useMemo(
1184
+ () => resolveFieldOverType(fieldConfig.formatter, inputConfig.formatter),
1185
+ [fieldConfig.formatter, inputConfig.formatter]
1186
+ );
1178
1187
  const handleChange = useCallback(
1179
1188
  (onChange) => (newValue) => {
1180
1189
  const parsedValue = parse(
1181
1190
  newValue,
1182
- inputConfig.parser,
1191
+ effectiveParser,
1183
1192
  providerConfig.parsers
1184
1193
  );
1185
1194
  onChange(parsedValue);
1186
1195
  changeField(name, parsedValue, inputConfig);
1187
1196
  },
1188
- [
1189
- inputConfig.parser,
1190
- providerConfig.parsers,
1191
- changeField,
1192
- name,
1193
- inputConfig
1194
- ]
1197
+ [effectiveParser, providerConfig.parsers, changeField, name, inputConfig]
1195
1198
  );
1196
1199
  const fieldStateRef = useRef(
1197
1200
  {}
@@ -1209,7 +1212,7 @@ function useField({
1209
1212
  render: ({ field, fieldState, formState }) => {
1210
1213
  const formattedValue = format(
1211
1214
  field.value,
1212
- inputConfig.formatter,
1215
+ effectiveFormatter,
1213
1216
  providerConfig.formatters
1214
1217
  );
1215
1218
  const stateInjection = {};