@vuehookform/core 0.1.1 → 0.1.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 vuehookform
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -8,6 +8,14 @@ export interface FieldArrayState {
8
8
  items: Ref<FieldArrayItem[]>;
9
9
  values: unknown[];
10
10
  }
11
+ /**
12
+ * Cached event handlers for a field to prevent recreation on every render
13
+ */
14
+ export interface FieldHandlers {
15
+ onInput: (e: Event) => Promise<void>;
16
+ onBlur: (e: Event) => Promise<void>;
17
+ refCallback: (el: unknown) => void;
18
+ }
11
19
  /**
12
20
  * Shared form context containing all reactive state
13
21
  * This is passed to sub-modules via dependency injection
@@ -16,14 +24,15 @@ export interface FormContext<FormValues> {
16
24
  formData: Record<string, unknown>;
17
25
  defaultValues: Record<string, unknown>;
18
26
  errors: ShallowRef<FieldErrors<FormValues>>;
19
- touchedFields: Ref<Record<string, boolean>>;
20
- dirtyFields: Ref<Record<string, boolean>>;
27
+ touchedFields: ShallowRef<Record<string, boolean>>;
28
+ dirtyFields: ShallowRef<Record<string, boolean>>;
21
29
  isSubmitting: Ref<boolean>;
22
30
  isLoading: Ref<boolean>;
23
31
  submitCount: Ref<number>;
24
32
  fieldRefs: Map<string, Ref<HTMLInputElement | null>>;
25
33
  fieldOptions: Map<string, RegisterOptions>;
26
34
  fieldArrays: Map<string, FieldArrayState>;
35
+ fieldHandlers: Map<string, FieldHandlers>;
27
36
  debounceTimers: Map<string, ReturnType<typeof setTimeout>>;
28
37
  validationRequestIds: Map<string, number>;
29
38
  options: UseFormOptions<ZodType>;
@@ -2,7 +2,7 @@
2
2
  * Get value from object using dot notation path
3
3
  * @example get({ user: { name: 'John' } }, 'user.name') => 'John'
4
4
  */
5
- export declare function get(obj: Record<string, unknown>, path: string): unknown;
5
+ export declare function get(obj: unknown, path: string): unknown;
6
6
  /**
7
7
  * Set value in object using dot notation path
8
8
  * @example set({}, 'user.name', 'John') => { user: { name: 'John' } }
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let vue = require("vue");
3
3
  function get(obj, path) {
4
- if (!path) return obj;
4
+ if (!path || obj === null || obj === void 0) return obj;
5
5
  const keys = path.split(".");
6
6
  let result = obj;
7
7
  for (const key of keys) {
@@ -68,14 +68,15 @@ function createFormContext(options) {
68
68
  formData,
69
69
  defaultValues,
70
70
  errors: (0, vue.shallowRef)({}),
71
- touchedFields: (0, vue.ref)({}),
72
- dirtyFields: (0, vue.ref)({}),
71
+ touchedFields: (0, vue.shallowRef)({}),
72
+ dirtyFields: (0, vue.shallowRef)({}),
73
73
  isSubmitting: (0, vue.ref)(false),
74
74
  isLoading,
75
75
  submitCount: (0, vue.ref)(0),
76
76
  fieldRefs: /* @__PURE__ */ new Map(),
77
77
  fieldOptions: /* @__PURE__ */ new Map(),
78
78
  fieldArrays: /* @__PURE__ */ new Map(),
79
+ fieldHandlers: /* @__PURE__ */ new Map(),
79
80
  debounceTimers: /* @__PURE__ */ new Map(),
80
81
  validationRequestIds: /* @__PURE__ */ new Map(),
81
82
  options
@@ -100,7 +101,9 @@ function groupErrorsByPath(issues) {
100
101
  return grouped;
101
102
  }
102
103
  function createFieldError(errors) {
103
- if (errors.length === 1) return errors[0].message;
104
+ const firstError = errors[0];
105
+ if (!firstError) return "";
106
+ if (errors.length === 1) return firstError.message;
104
107
  const types = {};
105
108
  for (const err of errors) {
106
109
  const existing = types[err.type];
@@ -108,8 +111,8 @@ function createFieldError(errors) {
108
111
  else types[err.type] = err.message;
109
112
  }
110
113
  return {
111
- type: errors[0].type,
112
- message: errors[0].message,
114
+ type: firstError.type,
115
+ message: firstError.message,
113
116
  types
114
117
  };
115
118
  }
@@ -131,7 +134,7 @@ function createValidation(ctx) {
131
134
  ctx.errors.value = clearFieldErrors(ctx.errors.value, fieldPath);
132
135
  return true;
133
136
  }
134
- let newErrors$1 = clearFieldErrors(ctx.errors.value, fieldPath);
137
+ const newErrors$1 = clearFieldErrors(ctx.errors.value, fieldPath);
135
138
  const grouped$1 = groupErrorsByPath(fieldErrors);
136
139
  for (const [path, errors] of grouped$1) set(newErrors$1, path, createFieldError(errors));
137
140
  ctx.errors.value = newErrors$1;
@@ -147,96 +150,112 @@ function createValidation(ctx) {
147
150
  }
148
151
  function createFieldRegistration(ctx, validate) {
149
152
  function register(name, registerOptions) {
150
- const fieldRef = (0, vue.ref)(null);
151
- ctx.fieldRefs.set(name, fieldRef);
152
- if (registerOptions) ctx.fieldOptions.set(name, registerOptions);
153
- if (get(ctx.formData, name) === void 0) {
154
- const defaultValue = get(ctx.defaultValues, name);
155
- if (defaultValue !== void 0) set(ctx.formData, name, defaultValue);
153
+ let fieldRef = ctx.fieldRefs.get(name);
154
+ if (!fieldRef) {
155
+ fieldRef = (0, vue.ref)(null);
156
+ ctx.fieldRefs.set(name, fieldRef);
157
+ if (get(ctx.formData, name) === void 0) {
158
+ const defaultValue = get(ctx.defaultValues, name);
159
+ if (defaultValue !== void 0) set(ctx.formData, name, defaultValue);
160
+ }
156
161
  }
157
- const runCustomValidation = async (fieldName, value, requestId) => {
158
- const fieldOpts = ctx.fieldOptions.get(fieldName);
159
- if (!fieldOpts?.validate || fieldOpts.disabled) return;
160
- const error = await fieldOpts.validate(value);
161
- if (requestId !== ctx.validationRequestIds.get(fieldName)) return;
162
- if (error) ctx.errors.value = {
163
- ...ctx.errors.value,
164
- [fieldName]: error
162
+ if (registerOptions) ctx.fieldOptions.set(name, registerOptions);
163
+ let handlers = ctx.fieldHandlers.get(name);
164
+ if (!handlers) {
165
+ const runCustomValidation = async (fieldName, value, requestId) => {
166
+ const fieldOpts = ctx.fieldOptions.get(fieldName);
167
+ if (!fieldOpts?.validate || fieldOpts.disabled) return;
168
+ const error = await fieldOpts.validate(value);
169
+ if (requestId !== ctx.validationRequestIds.get(fieldName)) return;
170
+ if (error) ctx.errors.value = {
171
+ ...ctx.errors.value,
172
+ [fieldName]: error
173
+ };
174
+ else {
175
+ const newErrors = { ...ctx.errors.value };
176
+ delete newErrors[fieldName];
177
+ ctx.errors.value = newErrors;
178
+ }
165
179
  };
166
- else {
167
- const newErrors = { ...ctx.errors.value };
168
- delete newErrors[fieldName];
169
- ctx.errors.value = newErrors;
170
- }
171
- };
172
- const onInput = async (e) => {
173
- const target = e.target;
174
- const value = target.type === "checkbox" ? target.checked : target.value;
175
- set(ctx.formData, name, value);
176
- ctx.dirtyFields.value = {
177
- ...ctx.dirtyFields.value,
178
- [name]: true
180
+ const onInput = async (e) => {
181
+ const target = e.target;
182
+ const value = target.type === "checkbox" ? target.checked : target.value;
183
+ set(ctx.formData, name, value);
184
+ ctx.dirtyFields.value = {
185
+ ...ctx.dirtyFields.value,
186
+ [name]: true
187
+ };
188
+ if (ctx.options.mode === "onChange" || ctx.options.mode === "onTouched" && ctx.touchedFields.value[name] || ctx.touchedFields.value[name] && ctx.options.reValidateMode === "onChange") await validate(name);
189
+ const fieldOpts = ctx.fieldOptions.get(name);
190
+ if (fieldOpts?.validate && !fieldOpts.disabled) {
191
+ const requestId = Date.now() + Math.random();
192
+ ctx.validationRequestIds.set(name, requestId);
193
+ const debounceMs = fieldOpts.validateDebounce || 0;
194
+ if (debounceMs > 0) {
195
+ const existingTimer = ctx.debounceTimers.get(name);
196
+ if (existingTimer) clearTimeout(existingTimer);
197
+ const timer = setTimeout(() => {
198
+ ctx.debounceTimers.delete(name);
199
+ runCustomValidation(name, value, requestId);
200
+ }, debounceMs);
201
+ ctx.debounceTimers.set(name, timer);
202
+ } else await runCustomValidation(name, value, requestId);
203
+ }
179
204
  };
180
- if (ctx.options.mode === "onChange" || ctx.options.mode === "onTouched" && ctx.touchedFields.value[name] || ctx.touchedFields.value[name] && ctx.options.reValidateMode === "onChange") await validate(name);
181
- const fieldOpts = ctx.fieldOptions.get(name);
182
- if (fieldOpts?.validate && !fieldOpts.disabled) {
183
- const requestId = Date.now() + Math.random();
184
- ctx.validationRequestIds.set(name, requestId);
185
- const debounceMs = fieldOpts.validateDebounce || 0;
186
- if (debounceMs > 0) {
187
- const existingTimer = ctx.debounceTimers.get(name);
188
- if (existingTimer) clearTimeout(existingTimer);
189
- const timer = setTimeout(() => {
190
- ctx.debounceTimers.delete(name);
191
- runCustomValidation(name, value, requestId);
192
- }, debounceMs);
193
- ctx.debounceTimers.set(name, timer);
194
- } else await runCustomValidation(name, value, requestId);
195
- }
196
- };
197
- const onBlur = async (_e) => {
198
- ctx.touchedFields.value = {
199
- ...ctx.touchedFields.value,
200
- [name]: true
205
+ const onBlur = async (_e) => {
206
+ ctx.touchedFields.value = {
207
+ ...ctx.touchedFields.value,
208
+ [name]: true
209
+ };
210
+ if (ctx.options.mode === "onBlur" || ctx.options.mode === "onTouched" || ctx.submitCount.value > 0 && ctx.options.reValidateMode === "onBlur") await validate(name);
201
211
  };
202
- if (ctx.options.mode === "onBlur" || ctx.options.mode === "onTouched" || ctx.submitCount.value > 0 && ctx.options.reValidateMode === "onBlur") await validate(name);
203
- };
204
- const refCallback = (el) => {
205
- const previousEl = fieldRef.value;
206
- fieldRef.value = el;
207
- if (el && !registerOptions?.controlled && el instanceof HTMLInputElement) {
208
- const value = get(ctx.formData, name);
209
- if (value !== void 0) if (el.type === "checkbox") el.checked = value;
210
- else el.value = value;
211
- }
212
- if (previousEl && !el) {
213
- if (registerOptions?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
214
- unset(ctx.formData, name);
215
- const newErrors = { ...ctx.errors.value };
216
- delete newErrors[name];
217
- ctx.errors.value = newErrors;
218
- const newTouched = { ...ctx.touchedFields.value };
219
- delete newTouched[name];
220
- ctx.touchedFields.value = newTouched;
221
- const newDirty = { ...ctx.dirtyFields.value };
222
- delete newDirty[name];
223
- ctx.dirtyFields.value = newDirty;
224
- ctx.fieldRefs.delete(name);
225
- ctx.fieldOptions.delete(name);
226
- const timer = ctx.debounceTimers.get(name);
227
- if (timer) {
228
- clearTimeout(timer);
229
- ctx.debounceTimers.delete(name);
212
+ const refCallback = (el) => {
213
+ const currentFieldRef = ctx.fieldRefs.get(name);
214
+ if (!currentFieldRef) return;
215
+ const previousEl = currentFieldRef.value;
216
+ currentFieldRef.value = el;
217
+ const opts = ctx.fieldOptions.get(name);
218
+ if (el && !opts?.controlled && el instanceof HTMLInputElement) {
219
+ const value = get(ctx.formData, name);
220
+ if (value !== void 0) if (el.type === "checkbox") el.checked = value;
221
+ else el.value = value;
222
+ }
223
+ if (previousEl && !el) {
224
+ if (opts?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
225
+ unset(ctx.formData, name);
226
+ const newErrors = { ...ctx.errors.value };
227
+ delete newErrors[name];
228
+ ctx.errors.value = newErrors;
229
+ const newTouched = { ...ctx.touchedFields.value };
230
+ delete newTouched[name];
231
+ ctx.touchedFields.value = newTouched;
232
+ const newDirty = { ...ctx.dirtyFields.value };
233
+ delete newDirty[name];
234
+ ctx.dirtyFields.value = newDirty;
235
+ ctx.fieldRefs.delete(name);
236
+ ctx.fieldOptions.delete(name);
237
+ ctx.fieldHandlers.delete(name);
238
+ const timer = ctx.debounceTimers.get(name);
239
+ if (timer) {
240
+ clearTimeout(timer);
241
+ ctx.debounceTimers.delete(name);
242
+ }
243
+ ctx.validationRequestIds.delete(name);
230
244
  }
231
- ctx.validationRequestIds.delete(name);
232
245
  }
233
- }
234
- };
246
+ };
247
+ handlers = {
248
+ onInput,
249
+ onBlur,
250
+ refCallback
251
+ };
252
+ ctx.fieldHandlers.set(name, handlers);
253
+ }
235
254
  return {
236
255
  name,
237
- ref: refCallback,
238
- onInput,
239
- onBlur,
256
+ ref: handlers.refCallback,
257
+ onInput: handlers.onInput,
258
+ onBlur: handlers.onBlur,
240
259
  ...registerOptions?.controlled && { value: (0, vue.computed)({
241
260
  get: () => get(ctx.formData, name),
242
261
  set: (val) => {
@@ -252,6 +271,7 @@ function createFieldRegistration(ctx, validate) {
252
271
  function unregister(name) {
253
272
  ctx.fieldRefs.delete(name);
254
273
  ctx.fieldOptions.delete(name);
274
+ ctx.fieldHandlers.delete(name);
255
275
  const timer = ctx.debounceTimers.get(name);
256
276
  if (timer) {
257
277
  clearTimeout(timer);
@@ -1,6 +1,6 @@
1
1
  import { computed, inject, provide, reactive, ref, shallowRef } from "vue";
2
2
  function get(obj, path) {
3
- if (!path) return obj;
3
+ if (!path || obj === null || obj === void 0) return obj;
4
4
  const keys = path.split(".");
5
5
  let result = obj;
6
6
  for (const key of keys) {
@@ -67,14 +67,15 @@ function createFormContext(options) {
67
67
  formData,
68
68
  defaultValues,
69
69
  errors: shallowRef({}),
70
- touchedFields: ref({}),
71
- dirtyFields: ref({}),
70
+ touchedFields: shallowRef({}),
71
+ dirtyFields: shallowRef({}),
72
72
  isSubmitting: ref(false),
73
73
  isLoading,
74
74
  submitCount: ref(0),
75
75
  fieldRefs: /* @__PURE__ */ new Map(),
76
76
  fieldOptions: /* @__PURE__ */ new Map(),
77
77
  fieldArrays: /* @__PURE__ */ new Map(),
78
+ fieldHandlers: /* @__PURE__ */ new Map(),
78
79
  debounceTimers: /* @__PURE__ */ new Map(),
79
80
  validationRequestIds: /* @__PURE__ */ new Map(),
80
81
  options
@@ -99,7 +100,9 @@ function groupErrorsByPath(issues) {
99
100
  return grouped;
100
101
  }
101
102
  function createFieldError(errors) {
102
- if (errors.length === 1) return errors[0].message;
103
+ const firstError = errors[0];
104
+ if (!firstError) return "";
105
+ if (errors.length === 1) return firstError.message;
103
106
  const types = {};
104
107
  for (const err of errors) {
105
108
  const existing = types[err.type];
@@ -107,8 +110,8 @@ function createFieldError(errors) {
107
110
  else types[err.type] = err.message;
108
111
  }
109
112
  return {
110
- type: errors[0].type,
111
- message: errors[0].message,
113
+ type: firstError.type,
114
+ message: firstError.message,
112
115
  types
113
116
  };
114
117
  }
@@ -130,7 +133,7 @@ function createValidation(ctx) {
130
133
  ctx.errors.value = clearFieldErrors(ctx.errors.value, fieldPath);
131
134
  return true;
132
135
  }
133
- let newErrors$1 = clearFieldErrors(ctx.errors.value, fieldPath);
136
+ const newErrors$1 = clearFieldErrors(ctx.errors.value, fieldPath);
134
137
  const grouped$1 = groupErrorsByPath(fieldErrors);
135
138
  for (const [path, errors] of grouped$1) set(newErrors$1, path, createFieldError(errors));
136
139
  ctx.errors.value = newErrors$1;
@@ -146,96 +149,112 @@ function createValidation(ctx) {
146
149
  }
147
150
  function createFieldRegistration(ctx, validate) {
148
151
  function register(name, registerOptions) {
149
- const fieldRef = ref(null);
150
- ctx.fieldRefs.set(name, fieldRef);
151
- if (registerOptions) ctx.fieldOptions.set(name, registerOptions);
152
- if (get(ctx.formData, name) === void 0) {
153
- const defaultValue = get(ctx.defaultValues, name);
154
- if (defaultValue !== void 0) set(ctx.formData, name, defaultValue);
152
+ let fieldRef = ctx.fieldRefs.get(name);
153
+ if (!fieldRef) {
154
+ fieldRef = ref(null);
155
+ ctx.fieldRefs.set(name, fieldRef);
156
+ if (get(ctx.formData, name) === void 0) {
157
+ const defaultValue = get(ctx.defaultValues, name);
158
+ if (defaultValue !== void 0) set(ctx.formData, name, defaultValue);
159
+ }
155
160
  }
156
- const runCustomValidation = async (fieldName, value, requestId) => {
157
- const fieldOpts = ctx.fieldOptions.get(fieldName);
158
- if (!fieldOpts?.validate || fieldOpts.disabled) return;
159
- const error = await fieldOpts.validate(value);
160
- if (requestId !== ctx.validationRequestIds.get(fieldName)) return;
161
- if (error) ctx.errors.value = {
162
- ...ctx.errors.value,
163
- [fieldName]: error
161
+ if (registerOptions) ctx.fieldOptions.set(name, registerOptions);
162
+ let handlers = ctx.fieldHandlers.get(name);
163
+ if (!handlers) {
164
+ const runCustomValidation = async (fieldName, value, requestId) => {
165
+ const fieldOpts = ctx.fieldOptions.get(fieldName);
166
+ if (!fieldOpts?.validate || fieldOpts.disabled) return;
167
+ const error = await fieldOpts.validate(value);
168
+ if (requestId !== ctx.validationRequestIds.get(fieldName)) return;
169
+ if (error) ctx.errors.value = {
170
+ ...ctx.errors.value,
171
+ [fieldName]: error
172
+ };
173
+ else {
174
+ const newErrors = { ...ctx.errors.value };
175
+ delete newErrors[fieldName];
176
+ ctx.errors.value = newErrors;
177
+ }
164
178
  };
165
- else {
166
- const newErrors = { ...ctx.errors.value };
167
- delete newErrors[fieldName];
168
- ctx.errors.value = newErrors;
169
- }
170
- };
171
- const onInput = async (e) => {
172
- const target = e.target;
173
- const value = target.type === "checkbox" ? target.checked : target.value;
174
- set(ctx.formData, name, value);
175
- ctx.dirtyFields.value = {
176
- ...ctx.dirtyFields.value,
177
- [name]: true
179
+ const onInput = async (e) => {
180
+ const target = e.target;
181
+ const value = target.type === "checkbox" ? target.checked : target.value;
182
+ set(ctx.formData, name, value);
183
+ ctx.dirtyFields.value = {
184
+ ...ctx.dirtyFields.value,
185
+ [name]: true
186
+ };
187
+ if (ctx.options.mode === "onChange" || ctx.options.mode === "onTouched" && ctx.touchedFields.value[name] || ctx.touchedFields.value[name] && ctx.options.reValidateMode === "onChange") await validate(name);
188
+ const fieldOpts = ctx.fieldOptions.get(name);
189
+ if (fieldOpts?.validate && !fieldOpts.disabled) {
190
+ const requestId = Date.now() + Math.random();
191
+ ctx.validationRequestIds.set(name, requestId);
192
+ const debounceMs = fieldOpts.validateDebounce || 0;
193
+ if (debounceMs > 0) {
194
+ const existingTimer = ctx.debounceTimers.get(name);
195
+ if (existingTimer) clearTimeout(existingTimer);
196
+ const timer = setTimeout(() => {
197
+ ctx.debounceTimers.delete(name);
198
+ runCustomValidation(name, value, requestId);
199
+ }, debounceMs);
200
+ ctx.debounceTimers.set(name, timer);
201
+ } else await runCustomValidation(name, value, requestId);
202
+ }
178
203
  };
179
- if (ctx.options.mode === "onChange" || ctx.options.mode === "onTouched" && ctx.touchedFields.value[name] || ctx.touchedFields.value[name] && ctx.options.reValidateMode === "onChange") await validate(name);
180
- const fieldOpts = ctx.fieldOptions.get(name);
181
- if (fieldOpts?.validate && !fieldOpts.disabled) {
182
- const requestId = Date.now() + Math.random();
183
- ctx.validationRequestIds.set(name, requestId);
184
- const debounceMs = fieldOpts.validateDebounce || 0;
185
- if (debounceMs > 0) {
186
- const existingTimer = ctx.debounceTimers.get(name);
187
- if (existingTimer) clearTimeout(existingTimer);
188
- const timer = setTimeout(() => {
189
- ctx.debounceTimers.delete(name);
190
- runCustomValidation(name, value, requestId);
191
- }, debounceMs);
192
- ctx.debounceTimers.set(name, timer);
193
- } else await runCustomValidation(name, value, requestId);
194
- }
195
- };
196
- const onBlur = async (_e) => {
197
- ctx.touchedFields.value = {
198
- ...ctx.touchedFields.value,
199
- [name]: true
204
+ const onBlur = async (_e) => {
205
+ ctx.touchedFields.value = {
206
+ ...ctx.touchedFields.value,
207
+ [name]: true
208
+ };
209
+ if (ctx.options.mode === "onBlur" || ctx.options.mode === "onTouched" || ctx.submitCount.value > 0 && ctx.options.reValidateMode === "onBlur") await validate(name);
200
210
  };
201
- if (ctx.options.mode === "onBlur" || ctx.options.mode === "onTouched" || ctx.submitCount.value > 0 && ctx.options.reValidateMode === "onBlur") await validate(name);
202
- };
203
- const refCallback = (el) => {
204
- const previousEl = fieldRef.value;
205
- fieldRef.value = el;
206
- if (el && !registerOptions?.controlled && el instanceof HTMLInputElement) {
207
- const value = get(ctx.formData, name);
208
- if (value !== void 0) if (el.type === "checkbox") el.checked = value;
209
- else el.value = value;
210
- }
211
- if (previousEl && !el) {
212
- if (registerOptions?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
213
- unset(ctx.formData, name);
214
- const newErrors = { ...ctx.errors.value };
215
- delete newErrors[name];
216
- ctx.errors.value = newErrors;
217
- const newTouched = { ...ctx.touchedFields.value };
218
- delete newTouched[name];
219
- ctx.touchedFields.value = newTouched;
220
- const newDirty = { ...ctx.dirtyFields.value };
221
- delete newDirty[name];
222
- ctx.dirtyFields.value = newDirty;
223
- ctx.fieldRefs.delete(name);
224
- ctx.fieldOptions.delete(name);
225
- const timer = ctx.debounceTimers.get(name);
226
- if (timer) {
227
- clearTimeout(timer);
228
- ctx.debounceTimers.delete(name);
211
+ const refCallback = (el) => {
212
+ const currentFieldRef = ctx.fieldRefs.get(name);
213
+ if (!currentFieldRef) return;
214
+ const previousEl = currentFieldRef.value;
215
+ currentFieldRef.value = el;
216
+ const opts = ctx.fieldOptions.get(name);
217
+ if (el && !opts?.controlled && el instanceof HTMLInputElement) {
218
+ const value = get(ctx.formData, name);
219
+ if (value !== void 0) if (el.type === "checkbox") el.checked = value;
220
+ else el.value = value;
221
+ }
222
+ if (previousEl && !el) {
223
+ if (opts?.shouldUnregister ?? ctx.options.shouldUnregister ?? false) {
224
+ unset(ctx.formData, name);
225
+ const newErrors = { ...ctx.errors.value };
226
+ delete newErrors[name];
227
+ ctx.errors.value = newErrors;
228
+ const newTouched = { ...ctx.touchedFields.value };
229
+ delete newTouched[name];
230
+ ctx.touchedFields.value = newTouched;
231
+ const newDirty = { ...ctx.dirtyFields.value };
232
+ delete newDirty[name];
233
+ ctx.dirtyFields.value = newDirty;
234
+ ctx.fieldRefs.delete(name);
235
+ ctx.fieldOptions.delete(name);
236
+ ctx.fieldHandlers.delete(name);
237
+ const timer = ctx.debounceTimers.get(name);
238
+ if (timer) {
239
+ clearTimeout(timer);
240
+ ctx.debounceTimers.delete(name);
241
+ }
242
+ ctx.validationRequestIds.delete(name);
229
243
  }
230
- ctx.validationRequestIds.delete(name);
231
244
  }
232
- }
233
- };
245
+ };
246
+ handlers = {
247
+ onInput,
248
+ onBlur,
249
+ refCallback
250
+ };
251
+ ctx.fieldHandlers.set(name, handlers);
252
+ }
234
253
  return {
235
254
  name,
236
- ref: refCallback,
237
- onInput,
238
- onBlur,
255
+ ref: handlers.refCallback,
256
+ onInput: handlers.onInput,
257
+ onBlur: handlers.onBlur,
239
258
  ...registerOptions?.controlled && { value: computed({
240
259
  get: () => get(ctx.formData, name),
241
260
  set: (val) => {
@@ -251,6 +270,7 @@ function createFieldRegistration(ctx, validate) {
251
270
  function unregister(name) {
252
271
  ctx.fieldRefs.delete(name);
253
272
  ctx.fieldOptions.delete(name);
273
+ ctx.fieldHandlers.delete(name);
254
274
  const timer = ctx.debounceTimers.get(name);
255
275
  if (timer) {
256
276
  clearTimeout(timer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vuehookform/core",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "TypeScript-first form library for Vue 3, inspired by React Hook Form. Form-level state management with Zod validation.",
5
5
  "type": "module",
6
6
  "main": "./dist/vuehookform.cjs",
@@ -50,12 +50,12 @@
50
50
  "hook",
51
51
  "react-hook-form"
52
52
  ],
53
- "author": "",
53
+ "author": "jonnasson",
54
54
  "license": "MIT",
55
55
  "bugs": {
56
56
  "url": "https://github.com/vuehookform/core/issues"
57
57
  },
58
- "homepage": "https://github.com/vuehookform/core#readme",
58
+ "homepage": "https://vuehookform.com",
59
59
  "peerDependencies": {
60
60
  "vue": "^3.3.0",
61
61
  "zod": "^3.0.0 || ^4.0.0"
@@ -81,6 +81,7 @@
81
81
  "prettier": "3.6.2",
82
82
  "typescript": "~5.9.0",
83
83
  "vite": "npm:rolldown-vite@latest",
84
+ "vite-plugin-compression2": "^2.4.0",
84
85
  "vite-plugin-dts": "^4.5.4",
85
86
  "vitest": "^4.0.16",
86
87
  "vue": "^3.5.25",