|
1 |
| -import { Derived, batch } from '@tanstack/store' |
| 1 | +import { Derived, Store, batch } from '@tanstack/store' |
2 | 2 | import {
|
3 | 3 | isStandardSchemaValidator,
|
4 | 4 | standardSchemaValidators,
|
5 | 5 | } from './standardSchemaValidator'
|
6 | 6 | import { defaultFieldMeta } from './metaHelper'
|
7 | 7 | import {
|
8 | 8 | determineFieldLevelErrorSourceAndValue,
|
| 9 | + evaluate, |
9 | 10 | getAsyncValidatorArray,
|
10 | 11 | getBy,
|
11 | 12 | getSyncValidatorArray,
|
@@ -864,6 +865,58 @@ export type AnyFieldMeta = FieldMeta<
|
864 | 865 | any
|
865 | 866 | >
|
866 | 867 |
|
| 868 | +export type FieldBaseState< |
| 869 | + TParentData, |
| 870 | + TName extends DeepKeys<TParentData>, |
| 871 | + TData extends DeepValue<TParentData, TName>, |
| 872 | + TOnMount extends undefined | FieldValidateOrFn<TParentData, TName, TData>, |
| 873 | + TOnChange extends undefined | FieldValidateOrFn<TParentData, TName, TData>, |
| 874 | + TOnChangeAsync extends |
| 875 | + | undefined |
| 876 | + | FieldAsyncValidateOrFn<TParentData, TName, TData>, |
| 877 | + TOnBlur extends undefined | FieldValidateOrFn<TParentData, TName, TData>, |
| 878 | + TOnBlurAsync extends |
| 879 | + | undefined |
| 880 | + | FieldAsyncValidateOrFn<TParentData, TName, TData>, |
| 881 | + TOnSubmit extends undefined | FieldValidateOrFn<TParentData, TName, TData>, |
| 882 | + TOnSubmitAsync extends |
| 883 | + | undefined |
| 884 | + | FieldAsyncValidateOrFn<TParentData, TName, TData>, |
| 885 | + TOnDynamic extends undefined | FieldValidateOrFn<TParentData, TName, TData>, |
| 886 | + TOnDynamicAsync extends |
| 887 | + | undefined |
| 888 | + | FieldAsyncValidateOrFn<TParentData, TName, TData>, |
| 889 | +> = { |
| 890 | + name: TName |
| 891 | + defaultMeta: |
| 892 | + | Partial< |
| 893 | + FieldMeta< |
| 894 | + TParentData, |
| 895 | + TName, |
| 896 | + TData, |
| 897 | + TOnMount, |
| 898 | + TOnChange, |
| 899 | + TOnChangeAsync, |
| 900 | + TOnBlur, |
| 901 | + TOnBlurAsync, |
| 902 | + TOnSubmit, |
| 903 | + TOnSubmitAsync, |
| 904 | + TOnDynamic, |
| 905 | + TOnDynamicAsync, |
| 906 | + any, |
| 907 | + any, |
| 908 | + any, |
| 909 | + any, |
| 910 | + any, |
| 911 | + any, |
| 912 | + any, |
| 913 | + any, |
| 914 | + any |
| 915 | + > |
| 916 | + > |
| 917 | + | undefined |
| 918 | +} |
| 919 | + |
867 | 920 | /**
|
868 | 921 | * An object type representing the state of a field.
|
869 | 922 | */
|
@@ -1049,10 +1102,31 @@ export class FieldApi<
|
1049 | 1102 | TFormOnServer,
|
1050 | 1103 | TParentSubmitMeta
|
1051 | 1104 | >['form']
|
| 1105 | + |
| 1106 | + baseStore: Store< |
| 1107 | + FieldBaseState< |
| 1108 | + TParentData, |
| 1109 | + TName, |
| 1110 | + TData, |
| 1111 | + TOnMount, |
| 1112 | + TOnChange, |
| 1113 | + TOnChangeAsync, |
| 1114 | + TOnBlur, |
| 1115 | + TOnBlurAsync, |
| 1116 | + TOnSubmit, |
| 1117 | + TOnSubmitAsync, |
| 1118 | + TOnDynamic, |
| 1119 | + TOnDynamicAsync |
| 1120 | + > |
| 1121 | + > |
| 1122 | + |
1052 | 1123 | /**
|
1053 | 1124 | * The field name.
|
1054 | 1125 | */
|
1055 |
| - name!: DeepKeys<TParentData> |
| 1126 | + get name(): DeepKeys<TParentData> { |
| 1127 | + return this.baseStore.state.name |
| 1128 | + } |
| 1129 | + |
1056 | 1130 | /**
|
1057 | 1131 | * The field options.
|
1058 | 1132 | */
|
@@ -1152,20 +1226,42 @@ export class FieldApi<
|
1152 | 1226 | >,
|
1153 | 1227 | ) {
|
1154 | 1228 | this.form = opts.form as never
|
1155 |
| - this.name = opts.name as never |
1156 | 1229 | this.timeoutIds = {
|
1157 | 1230 | validations: {} as Record<ValidationCause, never>,
|
1158 | 1231 | listeners: {} as Record<ListenerCause, never>,
|
1159 | 1232 | formListeners: {} as Record<ListenerCause, never>,
|
1160 | 1233 | }
|
1161 | 1234 |
|
| 1235 | + this.baseStore = new Store< |
| 1236 | + FieldBaseState< |
| 1237 | + TParentData, |
| 1238 | + TName, |
| 1239 | + TData, |
| 1240 | + TOnMount, |
| 1241 | + TOnChange, |
| 1242 | + TOnChangeAsync, |
| 1243 | + TOnBlur, |
| 1244 | + TOnBlurAsync, |
| 1245 | + TOnSubmit, |
| 1246 | + TOnSubmitAsync, |
| 1247 | + TOnDynamic, |
| 1248 | + TOnDynamicAsync |
| 1249 | + > |
| 1250 | + >({ |
| 1251 | + name: opts.name, |
| 1252 | + defaultMeta: opts.defaultMeta, |
| 1253 | + }) |
| 1254 | + |
1162 | 1255 | this.store = new Derived({
|
1163 |
| - deps: [this.form.store], |
1164 |
| - fn: () => { |
1165 |
| - const value = this.form.getFieldValue(this.name) |
1166 |
| - const meta = this.form.getFieldMeta(this.name) ?? { |
| 1256 | + deps: [this.form.store, this.baseStore], |
| 1257 | + fn: ({ currDepVals }) => { |
| 1258 | + const fieldName = currDepVals[1].name |
| 1259 | + const defaultMeta = currDepVals[1].defaultMeta |
| 1260 | + |
| 1261 | + const value = this.form.getFieldValue(fieldName) |
| 1262 | + const meta = this.form.getFieldMeta(fieldName) ?? { |
1167 | 1263 | ...defaultFieldMeta,
|
1168 |
| - ...opts.defaultMeta, |
| 1264 | + ...(defaultMeta ?? {}), |
1169 | 1265 | }
|
1170 | 1266 |
|
1171 | 1267 | return {
|
@@ -1232,11 +1328,14 @@ export class FieldApi<
|
1232 | 1328 | mount = () => {
|
1233 | 1329 | const cleanup = this.store.mount()
|
1234 | 1330 |
|
1235 |
| - if ((this.options.defaultValue as unknown) !== undefined) { |
1236 |
| - this.form.setFieldValue(this.name, this.options.defaultValue as never, { |
| 1331 | + if (this.options.defaultValue !== undefined) { |
| 1332 | + this.form.setFieldValue(this.name, this.options.defaultValue, { |
1237 | 1333 | dontUpdateMeta: true,
|
1238 | 1334 | })
|
1239 | 1335 | }
|
| 1336 | + if (this.options.name !== this.name) { |
| 1337 | + this.baseStore.setState((prev) => ({ ...prev, name: this.options.name })) |
| 1338 | + } |
1240 | 1339 |
|
1241 | 1340 | const info = this.getInfo()
|
1242 | 1341 | info.instance = this as never
|
@@ -1309,39 +1408,41 @@ export class FieldApi<
|
1309 | 1408 | TParentSubmitMeta
|
1310 | 1409 | >,
|
1311 | 1410 | ) => {
|
1312 |
| - this.options = opts as never |
| 1411 | + const shouldUpdateValue = |
| 1412 | + opts.defaultValue !== undefined && |
| 1413 | + !this.state.meta.isTouched && |
| 1414 | + !evaluate(opts.defaultValue, this.options.defaultValue) |
| 1415 | + |
| 1416 | + const shouldUpdateName = !evaluate(this.name, opts.name) |
| 1417 | + |
| 1418 | + const shouldUpdateMeta = !evaluate( |
| 1419 | + this.baseStore.state.defaultMeta, |
| 1420 | + opts.defaultMeta, |
| 1421 | + ) |
1313 | 1422 |
|
1314 |
| - const nameHasChanged = this.name !== opts.name |
1315 |
| - this.name = opts.name |
1316 |
| - |
1317 |
| - // Default Value |
1318 |
| - if ((this.state.value as unknown) === undefined) { |
1319 |
| - const formDefault = getBy( |
1320 |
| - opts.form.options.defaultValues, |
1321 |
| - opts.name, |
1322 |
| - ).value |
1323 |
| - |
1324 |
| - const defaultValue = (opts.defaultValue as unknown) ?? formDefault |
1325 |
| - |
1326 |
| - // The name is dynamic in array fields. It changes when the user performs operations like removing or reordering. |
1327 |
| - // In this case, we don't want to force a default value if the store managed to find an existing value. |
1328 |
| - |
1329 |
| - // TODO test what is actually needed here |
1330 |
| - // if (nameHasChanged) { |
1331 |
| - // this.setValue((val) => (val as unknown) || defaultValue, { |
1332 |
| - // dontUpdateMeta: true, |
1333 |
| - // }) |
1334 |
| - // } else if (defaultValue !== undefined) { |
1335 |
| - // this.setValue(defaultValue as never, { |
1336 |
| - // dontUpdateMeta: true, |
1337 |
| - // }) |
1338 |
| - // } |
| 1423 | + if (shouldUpdateValue) { |
| 1424 | + this.form.setFieldValue(this.name, this.options.defaultValue!, { |
| 1425 | + dontUpdateMeta: true, |
| 1426 | + }) |
| 1427 | + } |
| 1428 | + |
| 1429 | + if (shouldUpdateName) { |
| 1430 | + this.baseStore.setState((prev) => ({ ...prev, name: opts.name })) |
| 1431 | + } |
| 1432 | + |
| 1433 | + if (shouldUpdateMeta) { |
| 1434 | + this.baseStore.setState((prev) => ({ |
| 1435 | + ...prev, |
| 1436 | + defaultMeta: opts.defaultMeta, |
| 1437 | + })) |
1339 | 1438 | }
|
1340 | 1439 |
|
1341 | 1440 | // Default Meta
|
1342 | 1441 | if (this.form.getFieldMeta(this.name) === undefined) {
|
1343 | 1442 | this.setMeta(this.state.meta)
|
1344 | 1443 | }
|
| 1444 | + |
| 1445 | + this.options = opts as never |
1345 | 1446 | }
|
1346 | 1447 |
|
1347 | 1448 | /**
|
|
0 commit comments