notform 1.0.7 → 2.0.0-alpha.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/LICENSE +1 -1
- package/README.md +23 -10
- package/dist/index.d.ts +2188 -380
- package/dist/index.js +0 -569
- package/package.json +25 -36
package/dist/index.js
CHANGED
|
@@ -1,569 +0,0 @@
|
|
|
1
|
-
import { computed, createBlock, createTextVNode, defineComponent, guardReactiveProps, inject, mergeProps, nextTick, normalizeProps, onMounted, openBlock, provide, reactive, ref, renderSlot, resolveDynamicComponent, toDisplayString, toValue, unref, useAttrs, useId, vShow, withCtx, withDirectives } from "vue";
|
|
2
|
-
import { getProperty, parsePath, setProperty } from "dot-prop";
|
|
3
|
-
import { isEqual } from "es-toolkit/predicate";
|
|
4
|
-
|
|
5
|
-
//#region src/utils/not-form-context.ts
|
|
6
|
-
/** Internal registry mapping form IDs to their injection keys */
|
|
7
|
-
const notFormContextKeys = /* @__PURE__ */ new Map();
|
|
8
|
-
/** Injection key for the unique identifier of the current active form */
|
|
9
|
-
const CURRENT_NOT_FORM_ID_KEY = Symbol("notform:id");
|
|
10
|
-
/**
|
|
11
|
-
* Retrieves an existing injection key or creates a new one for a form ID.
|
|
12
|
-
* @template TSchema The validation schema type derived from ObjectSchema.
|
|
13
|
-
* @param id The unique string identifier for the form.
|
|
14
|
-
* @returns A strictly typed InjectionKey for the form context.
|
|
15
|
-
*/
|
|
16
|
-
function getNotFormContextKey(id) {
|
|
17
|
-
if (!notFormContextKeys.has(id)) {
|
|
18
|
-
const key = Symbol(`notform:${id}:context`);
|
|
19
|
-
notFormContextKeys.set(id, key);
|
|
20
|
-
}
|
|
21
|
-
return notFormContextKeys.get(id);
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Locates and returns the active form context associated with a given ID.
|
|
25
|
-
* @template TSchema The validation schema type derived from ObjectSchema.
|
|
26
|
-
* @param id The unique identifier of the form to access.
|
|
27
|
-
* @returns The resolved form context object.
|
|
28
|
-
* @throws Error if the context cannot be found in the current component tree.
|
|
29
|
-
*/
|
|
30
|
-
function withContext(id) {
|
|
31
|
-
const context = inject(getNotFormContextKey(id));
|
|
32
|
-
if (!context) throw new Error(`No form context found for form with id "${id}"`);
|
|
33
|
-
return context;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
//#endregion
|
|
37
|
-
//#region src/components/not-form.vue?vue&type=script&setup=true&lang.ts
|
|
38
|
-
/**
|
|
39
|
-
* Provides form context and a structural wrapper for form fields.
|
|
40
|
-
* @template TSchema The validation schema type derived from ObjectSchema.
|
|
41
|
-
*/
|
|
42
|
-
var not_form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
43
|
-
__name: "not-form",
|
|
44
|
-
props: {
|
|
45
|
-
id: {
|
|
46
|
-
type: String,
|
|
47
|
-
required: true
|
|
48
|
-
},
|
|
49
|
-
asChild: {
|
|
50
|
-
type: Boolean,
|
|
51
|
-
required: false,
|
|
52
|
-
default: false
|
|
53
|
-
},
|
|
54
|
-
as: {
|
|
55
|
-
type: String,
|
|
56
|
-
required: false,
|
|
57
|
-
default: "form"
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
setup(__props) {
|
|
61
|
-
const props = __props;
|
|
62
|
-
/**
|
|
63
|
-
* Slots provided by the Form component.
|
|
64
|
-
*/
|
|
65
|
-
const attributes = useAttrs();
|
|
66
|
-
const form = withContext(props.id);
|
|
67
|
-
const mergedAttrs = computed(() => ({
|
|
68
|
-
...attributes,
|
|
69
|
-
id: form.id
|
|
70
|
-
}));
|
|
71
|
-
provide(CURRENT_NOT_FORM_ID_KEY, form.id);
|
|
72
|
-
return (_ctx, _cache) => {
|
|
73
|
-
return props.asChild ? renderSlot(_ctx.$slots, "default", normalizeProps(mergeProps({ key: 0 }, {
|
|
74
|
-
...unref(form),
|
|
75
|
-
attributes: mergedAttrs.value
|
|
76
|
-
}))) : (openBlock(), createBlock(resolveDynamicComponent(props.as), normalizeProps(mergeProps({ key: 1 }, mergedAttrs.value)), {
|
|
77
|
-
default: withCtx(() => [renderSlot(_ctx.$slots, "default", normalizeProps(guardReactiveProps(unref(form))))]),
|
|
78
|
-
_: 3
|
|
79
|
-
}, 16));
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
//#endregion
|
|
85
|
-
//#region src/components/not-form.vue
|
|
86
|
-
var not_form_default = not_form_vue_vue_type_script_setup_true_lang_default;
|
|
87
|
-
|
|
88
|
-
//#endregion
|
|
89
|
-
//#region src/components/not-field.vue?vue&type=script&setup=true&lang.ts
|
|
90
|
-
/**
|
|
91
|
-
* Component for individual form fields.
|
|
92
|
-
* Manages field-level state, validation triggers, and events.
|
|
93
|
-
*/
|
|
94
|
-
var not_field_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
95
|
-
__name: "not-field",
|
|
96
|
-
props: { name: {
|
|
97
|
-
type: String,
|
|
98
|
-
required: true
|
|
99
|
-
} },
|
|
100
|
-
setup(__props) {
|
|
101
|
-
const props = __props;
|
|
102
|
-
/**
|
|
103
|
-
* Slots provided by the NotField component.
|
|
104
|
-
*/
|
|
105
|
-
const formID = inject(CURRENT_NOT_FORM_ID_KEY);
|
|
106
|
-
if (!formID) throw new Error("NotField must be used inside a NotForm component");
|
|
107
|
-
const { mode, validateOn, touchedFields, dirtyFields, validateField, getFieldErrors, touchField, dirtyField } = withContext(formID);
|
|
108
|
-
/**
|
|
109
|
-
* Triggers validation for this specific field.
|
|
110
|
-
* @returns Promise resolving to the validation result.
|
|
111
|
-
*/
|
|
112
|
-
async function validate() {
|
|
113
|
-
return await validateField(props.name);
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* The consolidated reactive state exposed to the component's template.
|
|
117
|
-
*/
|
|
118
|
-
const context = reactive({
|
|
119
|
-
name: computed(() => props.name),
|
|
120
|
-
errors: computed(() => getFieldErrors(props.name).map((error) => error.message)),
|
|
121
|
-
isTouched: computed(() => touchedFields.value.has(props.name)),
|
|
122
|
-
isDirty: computed(() => dirtyFields.value.has(props.name)),
|
|
123
|
-
isValid: computed(() => getFieldErrors(props.name).length === 0),
|
|
124
|
-
validate,
|
|
125
|
-
methods: {
|
|
126
|
-
onBlur: function() {
|
|
127
|
-
touchField(props.name);
|
|
128
|
-
if (!validateOn.includes("blur")) return;
|
|
129
|
-
validate();
|
|
130
|
-
},
|
|
131
|
-
onChange: function() {
|
|
132
|
-
dirtyField(props.name);
|
|
133
|
-
if (!validateOn.includes("change")) return;
|
|
134
|
-
if (mode === "lazy") return;
|
|
135
|
-
if (mode === "eager" && getFieldErrors(props.name).length > 0) validate();
|
|
136
|
-
},
|
|
137
|
-
onInput: function() {
|
|
138
|
-
dirtyField(props.name);
|
|
139
|
-
if (!validateOn.includes("input")) return;
|
|
140
|
-
if (mode === "lazy") return;
|
|
141
|
-
if (mode === "eager" && getFieldErrors(props.name).length > 0) validate();
|
|
142
|
-
},
|
|
143
|
-
onFocus: function() {
|
|
144
|
-
if (!validateOn.includes("focus")) return;
|
|
145
|
-
validate();
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
onMounted(async () => {
|
|
150
|
-
await nextTick();
|
|
151
|
-
if (validateOn.includes("mount")) await validate();
|
|
152
|
-
});
|
|
153
|
-
return (_ctx, _cache) => {
|
|
154
|
-
return renderSlot(_ctx.$slots, "default", normalizeProps(guardReactiveProps(context)));
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
//#endregion
|
|
160
|
-
//#region src/components/not-field.vue
|
|
161
|
-
var not_field_default = not_field_vue_vue_type_script_setup_true_lang_default;
|
|
162
|
-
|
|
163
|
-
//#endregion
|
|
164
|
-
//#region src/components/not-message.vue?vue&type=script&setup=true&lang.ts
|
|
165
|
-
/**
|
|
166
|
-
* Displays validation error messages for a specific form field.
|
|
167
|
-
* @template TSchema The validation schema type derived from ObjectSchema.
|
|
168
|
-
*/
|
|
169
|
-
var not_message_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
170
|
-
__name: "not-message",
|
|
171
|
-
props: {
|
|
172
|
-
name: {
|
|
173
|
-
type: String,
|
|
174
|
-
required: true
|
|
175
|
-
},
|
|
176
|
-
as: {
|
|
177
|
-
type: String,
|
|
178
|
-
required: false,
|
|
179
|
-
default: "span"
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
setup(__props) {
|
|
183
|
-
const props = __props;
|
|
184
|
-
/**
|
|
185
|
-
* Slots provided by the Message component.
|
|
186
|
-
*/
|
|
187
|
-
const attributes = useAttrs();
|
|
188
|
-
const formID = inject(CURRENT_NOT_FORM_ID_KEY);
|
|
189
|
-
if (!formID) throw new Error("Message must be used inside a NotForm component");
|
|
190
|
-
const { getFieldErrors } = withContext(formID);
|
|
191
|
-
/**
|
|
192
|
-
* The reactive state provided to the component's slot.
|
|
193
|
-
*/
|
|
194
|
-
const context = reactive({ message: computed(() => getFieldErrors(props.name).map((error) => error.message)[0]) });
|
|
195
|
-
const mergedAttrs = computed(() => ({
|
|
196
|
-
...context,
|
|
197
|
-
attributes
|
|
198
|
-
}));
|
|
199
|
-
return (_ctx, _cache) => {
|
|
200
|
-
return renderSlot(_ctx.$slots, "default", normalizeProps(guardReactiveProps(mergedAttrs.value)), () => [withDirectives((openBlock(), createBlock(resolveDynamicComponent(props.as), normalizeProps(guardReactiveProps(unref(attributes))), {
|
|
201
|
-
default: withCtx(() => [createTextVNode(toDisplayString(context.message), 1)]),
|
|
202
|
-
_: 1
|
|
203
|
-
}, 16)), [[vShow, context.message]])]);
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
//#endregion
|
|
209
|
-
//#region src/components/not-message.vue
|
|
210
|
-
var not_message_default = not_message_vue_vue_type_script_setup_true_lang_default;
|
|
211
|
-
|
|
212
|
-
//#endregion
|
|
213
|
-
//#region src/components/not-array-field.vue?vue&type=script&setup=true&lang.ts
|
|
214
|
-
/**
|
|
215
|
-
* Component for managing array-based form fields.
|
|
216
|
-
* Provides methods for adding, removing, and updating array items.
|
|
217
|
-
* @template TArraySchema Schema for the array field.
|
|
218
|
-
* @template TObjectSchema Parent form object schema.
|
|
219
|
-
*/
|
|
220
|
-
var not_array_field_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
221
|
-
__name: "not-array-field",
|
|
222
|
-
props: {
|
|
223
|
-
name: {
|
|
224
|
-
type: String,
|
|
225
|
-
required: true
|
|
226
|
-
},
|
|
227
|
-
schema: {
|
|
228
|
-
type: null,
|
|
229
|
-
required: true
|
|
230
|
-
}
|
|
231
|
-
},
|
|
232
|
-
setup(__props) {
|
|
233
|
-
const props = __props;
|
|
234
|
-
/**
|
|
235
|
-
* Slots provided by the NotArrayField component.
|
|
236
|
-
*/
|
|
237
|
-
const formID = inject(CURRENT_NOT_FORM_ID_KEY);
|
|
238
|
-
if (!formID) throw new Error("NotArrayField must be used inside a NotForm component");
|
|
239
|
-
const { state, validateField, getFieldErrors } = withContext(formID);
|
|
240
|
-
/**
|
|
241
|
-
* Reactive bridge between the form state and the specific array field.
|
|
242
|
-
*/
|
|
243
|
-
const arrayState = computed({
|
|
244
|
-
get() {
|
|
245
|
-
const fieldValue = getProperty(state.value, props.name);
|
|
246
|
-
return Array.isArray(fieldValue) ? fieldValue : [];
|
|
247
|
-
},
|
|
248
|
-
set(value) {
|
|
249
|
-
setProperty(state.value, props.name, value);
|
|
250
|
-
validateField(props.name);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
/**
|
|
254
|
-
* Computes individual field contexts for each item in the array.
|
|
255
|
-
*/
|
|
256
|
-
const fields = computed(() => {
|
|
257
|
-
return arrayState.value.map((value, index) => {
|
|
258
|
-
return {
|
|
259
|
-
key: index,
|
|
260
|
-
index,
|
|
261
|
-
value,
|
|
262
|
-
first: index === 0,
|
|
263
|
-
last: index === arrayState.value.length - 1
|
|
264
|
-
};
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
/**
|
|
268
|
-
* Adds a new item to the end of the array.
|
|
269
|
-
* @param data The initial data for the new item.
|
|
270
|
-
*/
|
|
271
|
-
function append(data) {
|
|
272
|
-
arrayState.value = [...arrayState.value, data];
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Adds a new item to the beginning of the array.
|
|
276
|
-
* @param data The initial data for the new item.
|
|
277
|
-
*/
|
|
278
|
-
function prepend(data) {
|
|
279
|
-
arrayState.value = [data, ...arrayState.value];
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Removes the item at the specified index.
|
|
283
|
-
* @param index The index of the item to remove.
|
|
284
|
-
*/
|
|
285
|
-
function remove(index) {
|
|
286
|
-
const newArray = [...arrayState.value];
|
|
287
|
-
newArray.splice(index, 1);
|
|
288
|
-
arrayState.value = newArray;
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* Inserts a new item at a specific index.
|
|
292
|
-
* @param index The insertion index.
|
|
293
|
-
* @param data The initial data for the new item.
|
|
294
|
-
*/
|
|
295
|
-
function insert(index, data) {
|
|
296
|
-
const newArray = [...arrayState.value];
|
|
297
|
-
newArray.splice(index, 0, data);
|
|
298
|
-
arrayState.value = newArray;
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Replaces the data at a specific index.
|
|
302
|
-
* @param index The target index.
|
|
303
|
-
* @param data The new data to apply.
|
|
304
|
-
*/
|
|
305
|
-
function update(index, data) {
|
|
306
|
-
const newArray = [...arrayState.value];
|
|
307
|
-
newArray[index] = data;
|
|
308
|
-
arrayState.value = newArray;
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* Reactive context object exposed to the NotArrayField slot.
|
|
312
|
-
*/
|
|
313
|
-
const context = reactive({
|
|
314
|
-
name: computed(() => props.name),
|
|
315
|
-
errors: computed(() => getFieldErrors(props.name).map((error) => error.message)),
|
|
316
|
-
fields,
|
|
317
|
-
append,
|
|
318
|
-
prepend,
|
|
319
|
-
remove,
|
|
320
|
-
insert,
|
|
321
|
-
update
|
|
322
|
-
});
|
|
323
|
-
return (_ctx, _cache) => {
|
|
324
|
-
return renderSlot(_ctx.$slots, "default", normalizeProps(guardReactiveProps(context)));
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
//#endregion
|
|
330
|
-
//#region src/components/not-array-field.vue
|
|
331
|
-
var not_array_field_default = not_array_field_vue_vue_type_script_setup_true_lang_default;
|
|
332
|
-
|
|
333
|
-
//#endregion
|
|
334
|
-
//#region src/utils/helpers.ts
|
|
335
|
-
/**
|
|
336
|
-
* Normalizes a validation path segment into a standard property key.
|
|
337
|
-
* @param segment The path segment to normalize.
|
|
338
|
-
* @returns The normalized key.
|
|
339
|
-
*/
|
|
340
|
-
function normalizeSegment(segment) {
|
|
341
|
-
if (typeof segment === "object" && segment !== null && "key" in segment) return segment.key;
|
|
342
|
-
return segment;
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Checks if a validation issue path matches a target field path.
|
|
346
|
-
* @param issuePath The path array from the validation issue.
|
|
347
|
-
* @param targetPath The normalized path to compare against.
|
|
348
|
-
* @returns True if the paths are equivalent.
|
|
349
|
-
*/
|
|
350
|
-
function isIssuePathEqual(issuePath, targetPath) {
|
|
351
|
-
if (!issuePath) return false;
|
|
352
|
-
if (issuePath.length !== targetPath.length) return false;
|
|
353
|
-
return issuePath.every((segment, i) => {
|
|
354
|
-
const normalizedSegment = normalizeSegment(segment);
|
|
355
|
-
const targetSegment = targetPath[i];
|
|
356
|
-
if (typeof normalizedSegment === "number" || typeof targetSegment === "number") return Number(normalizedSegment) === Number(targetSegment);
|
|
357
|
-
return normalizedSegment === targetSegment;
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* Recursively retrieves all reachable paths of an object as dot-separated strings.
|
|
362
|
-
* @param object The object to traverse.
|
|
363
|
-
* @param prefix An optional prefix to prepended to each generated path.
|
|
364
|
-
* @returns An array of string paths representing the object's structure.
|
|
365
|
-
*/
|
|
366
|
-
function getObjectPaths(object, prefix = "") {
|
|
367
|
-
let paths = [];
|
|
368
|
-
if (typeof object !== "object" || object === null) return paths;
|
|
369
|
-
for (const key in object) if (Object.prototype.hasOwnProperty.call(object, key)) {
|
|
370
|
-
const value = object[key];
|
|
371
|
-
const isArray = Array.isArray(object);
|
|
372
|
-
const isInt = /^\d+$/.test(key);
|
|
373
|
-
let currentKey = key;
|
|
374
|
-
if (isArray && isInt) currentKey = `[${key}]`;
|
|
375
|
-
const path = prefix ? prefix.endsWith("]") || currentKey.startsWith("[") ? `${prefix}${currentKey}` : `${prefix}.${currentKey}` : currentKey;
|
|
376
|
-
paths.push(path);
|
|
377
|
-
if (typeof value === "object" && value !== null) paths = paths.concat(getObjectPaths(value, path));
|
|
378
|
-
}
|
|
379
|
-
return paths;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
//#endregion
|
|
383
|
-
//#region src/composables/use-not-form.ts
|
|
384
|
-
/**
|
|
385
|
-
* Initializes a form instance with validation logic, reactive state, and lifecycle methods.
|
|
386
|
-
* @template TSchema The validation schema type derived from ObjectSchema.
|
|
387
|
-
* @param options Configuration object for the form behavior and initial state.
|
|
388
|
-
* @returns The gathered form context object.
|
|
389
|
-
*/
|
|
390
|
-
function useNotForm(options) {
|
|
391
|
-
const { id = useId(), schema: _schema, initialErrors: _initialErrors = [], initialState: _initialState = {}, mode = "eager", validateOn = [
|
|
392
|
-
"blur",
|
|
393
|
-
"input",
|
|
394
|
-
"change"
|
|
395
|
-
], onValidate, onReset, onError, onSubmit } = options;
|
|
396
|
-
/** Active validation schema reference */
|
|
397
|
-
const schema = computed(() => toValue(_schema));
|
|
398
|
-
/** Baseline reactive state at creation */
|
|
399
|
-
const initialState = structuredClone(_initialState);
|
|
400
|
-
/** Baseline validation issues at creation */
|
|
401
|
-
const initialErrors = structuredClone(_initialErrors);
|
|
402
|
-
/** Internal state set by last custom reset */
|
|
403
|
-
const onResetState = ref(null);
|
|
404
|
-
/** Internal errors set by last custom reset */
|
|
405
|
-
const onResetErrors = ref(null);
|
|
406
|
-
/** The unified reactive context for the form instance */
|
|
407
|
-
const context = {
|
|
408
|
-
id,
|
|
409
|
-
schema,
|
|
410
|
-
initialState,
|
|
411
|
-
initialErrors,
|
|
412
|
-
mode,
|
|
413
|
-
validateOn,
|
|
414
|
-
state: ref(structuredClone(initialState)),
|
|
415
|
-
errors: ref([...initialErrors]),
|
|
416
|
-
isValidating: ref(false),
|
|
417
|
-
isSubmitting: ref(false),
|
|
418
|
-
touchedFields: ref(/* @__PURE__ */ new Set()),
|
|
419
|
-
dirtyFields: ref(/* @__PURE__ */ new Set()),
|
|
420
|
-
isValid: computed(() => context.errors.value.length === 0),
|
|
421
|
-
isTouched: computed(() => context.touchedFields.value.size > 0),
|
|
422
|
-
isDirty: computed(() => context.dirtyFields.value.size > 0),
|
|
423
|
-
async validate() {
|
|
424
|
-
context.isValidating.value = true;
|
|
425
|
-
try {
|
|
426
|
-
const result = await context.schema.value["~standard"].validate(context.state.value);
|
|
427
|
-
if (result.issues) {
|
|
428
|
-
context.errors.value = [...result.issues];
|
|
429
|
-
onError?.([...result.issues]);
|
|
430
|
-
return { issues: result.issues };
|
|
431
|
-
}
|
|
432
|
-
if (onValidate) {
|
|
433
|
-
const customResult = await onValidate(result.value);
|
|
434
|
-
if (customResult === false) {
|
|
435
|
-
const formLevelError = {
|
|
436
|
-
message: "Validation failed",
|
|
437
|
-
path: []
|
|
438
|
-
};
|
|
439
|
-
context.errors.value = [formLevelError];
|
|
440
|
-
onError?.([formLevelError]);
|
|
441
|
-
return { issues: [formLevelError] };
|
|
442
|
-
}
|
|
443
|
-
if (Array.isArray(customResult) && customResult.length > 0) {
|
|
444
|
-
context.errors.value = customResult;
|
|
445
|
-
onError?.(customResult);
|
|
446
|
-
return { issues: customResult };
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
context.errors.value = [];
|
|
450
|
-
return { value: result.value };
|
|
451
|
-
} finally {
|
|
452
|
-
context.isValidating.value = false;
|
|
453
|
-
}
|
|
454
|
-
},
|
|
455
|
-
async validateField(path) {
|
|
456
|
-
context.isValidating.value = true;
|
|
457
|
-
try {
|
|
458
|
-
const result = await context.schema.value["~standard"].validate(context.state.value);
|
|
459
|
-
const pathArray = parsePath(path);
|
|
460
|
-
context.errors.value = context.errors.value.filter((error) => !isIssuePathEqual(error.path, pathArray));
|
|
461
|
-
if (result.issues) {
|
|
462
|
-
const errorsForPath = result.issues.filter((error) => isIssuePathEqual(error.path, pathArray));
|
|
463
|
-
if (errorsForPath.length > 0) {
|
|
464
|
-
context.errors.value = [...context.errors.value, ...errorsForPath];
|
|
465
|
-
return { issues: errorsForPath };
|
|
466
|
-
}
|
|
467
|
-
return { value: getProperty(context.state.value, path) };
|
|
468
|
-
}
|
|
469
|
-
return { value: getProperty(result.value, path) };
|
|
470
|
-
} finally {
|
|
471
|
-
context.isValidating.value = false;
|
|
472
|
-
}
|
|
473
|
-
},
|
|
474
|
-
reset(_state, _errors, _validate = false) {
|
|
475
|
-
if (_state) {
|
|
476
|
-
onResetState.value = structuredClone(_state);
|
|
477
|
-
context.state.value = structuredClone(_state);
|
|
478
|
-
} else {
|
|
479
|
-
onResetState.value = null;
|
|
480
|
-
context.state.value = structuredClone(initialState);
|
|
481
|
-
}
|
|
482
|
-
if (_errors) {
|
|
483
|
-
onResetErrors.value = structuredClone(_errors);
|
|
484
|
-
context.errors.value = [..._errors];
|
|
485
|
-
} else {
|
|
486
|
-
onResetErrors.value = null;
|
|
487
|
-
context.errors.value = [...initialErrors];
|
|
488
|
-
}
|
|
489
|
-
context.touchedFields.value.clear();
|
|
490
|
-
context.dirtyFields.value.clear();
|
|
491
|
-
onReset?.();
|
|
492
|
-
if (_validate) context.validate();
|
|
493
|
-
},
|
|
494
|
-
setState(_state, _validate = true) {
|
|
495
|
-
context.state.value = {
|
|
496
|
-
...context.state.value,
|
|
497
|
-
..._state
|
|
498
|
-
};
|
|
499
|
-
getObjectPaths(_state).forEach((path) => {
|
|
500
|
-
context.touchedFields.value.add(path);
|
|
501
|
-
if (!isEqual(getProperty(context.state.value, path), getProperty(onResetState.value ?? initialState, path))) context.dirtyFields.value.add(path);
|
|
502
|
-
else context.dirtyFields.value.delete(path);
|
|
503
|
-
if (_validate) context.validateField(path);
|
|
504
|
-
});
|
|
505
|
-
},
|
|
506
|
-
setErrors(_errors) {
|
|
507
|
-
context.errors.value = [...context.errors.value, ..._errors];
|
|
508
|
-
},
|
|
509
|
-
clearErrors() {
|
|
510
|
-
context.errors.value = [];
|
|
511
|
-
},
|
|
512
|
-
getFieldErrors(field) {
|
|
513
|
-
const pathArray = parsePath(field);
|
|
514
|
-
return context.errors.value.filter((error) => isIssuePathEqual(error.path, pathArray));
|
|
515
|
-
},
|
|
516
|
-
touchField(field) {
|
|
517
|
-
context.touchedFields.value.add(field);
|
|
518
|
-
},
|
|
519
|
-
touchAllFields() {
|
|
520
|
-
const paths = getObjectPaths(context.state.value);
|
|
521
|
-
context.touchedFields.value = new Set(paths);
|
|
522
|
-
},
|
|
523
|
-
dirtyField(field) {
|
|
524
|
-
if (!isEqual(getProperty(context.state.value, field), getProperty(onResetState.value ?? initialState, field))) context.dirtyFields.value.add(field);
|
|
525
|
-
else context.dirtyFields.value.delete(field);
|
|
526
|
-
},
|
|
527
|
-
dirtyAllFields() {
|
|
528
|
-
const paths = getObjectPaths(context.state.value);
|
|
529
|
-
const newDirty = /* @__PURE__ */ new Set();
|
|
530
|
-
paths.forEach((path) => {
|
|
531
|
-
if (!isEqual(getProperty(context.state.value, path), getProperty(onResetState.value ?? initialState, path))) newDirty.add(path);
|
|
532
|
-
});
|
|
533
|
-
context.dirtyFields.value = newDirty;
|
|
534
|
-
},
|
|
535
|
-
async submit(event) {
|
|
536
|
-
context.isSubmitting.value = true;
|
|
537
|
-
try {
|
|
538
|
-
context.touchAllFields();
|
|
539
|
-
context.dirtyAllFields();
|
|
540
|
-
const result = await context.validate();
|
|
541
|
-
if (result.issues) {
|
|
542
|
-
event.preventDefault();
|
|
543
|
-
const errors = [...result.issues];
|
|
544
|
-
context.errors.value = errors;
|
|
545
|
-
onError?.(errors);
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
if (onSubmit) {
|
|
549
|
-
event.preventDefault();
|
|
550
|
-
await onSubmit(result.value);
|
|
551
|
-
}
|
|
552
|
-
} catch {
|
|
553
|
-
event.preventDefault();
|
|
554
|
-
onError?.([{
|
|
555
|
-
message: "An unexpected error occurred during submission",
|
|
556
|
-
path: []
|
|
557
|
-
}]);
|
|
558
|
-
} finally {
|
|
559
|
-
context.isSubmitting.value = false;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
};
|
|
563
|
-
provide(getNotFormContextKey(id), context);
|
|
564
|
-
return context;
|
|
565
|
-
}
|
|
566
|
-
var use_not_form_default = useNotForm;
|
|
567
|
-
|
|
568
|
-
//#endregion
|
|
569
|
-
export { not_array_field_default as NotArrayField, not_field_default as NotField, not_form_default as NotForm, not_message_default as NotMessage, use_not_form_default as useNotForm };
|
package/package.json
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notform",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-alpha.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
|
-
"publishConfig": {
|
|
7
|
-
"access": "public"
|
|
8
|
-
},
|
|
9
6
|
"description": "Vue Forms Without the Friction",
|
|
10
7
|
"author": "Favour Emeka <favorodera@gmail.com>",
|
|
11
8
|
"license": "MIT",
|
|
@@ -17,14 +14,8 @@
|
|
|
17
14
|
"bugs": {
|
|
18
15
|
"url": "https://github.com/favorodera/notform/issues"
|
|
19
16
|
},
|
|
20
|
-
"module": "./dist/index.js",
|
|
21
|
-
"main": "./dist/index.js",
|
|
22
17
|
"exports": {
|
|
23
|
-
".":
|
|
24
|
-
"types": "./dist/index.d.ts",
|
|
25
|
-
"import": "./dist/index.js",
|
|
26
|
-
"default": "./dist/index.js"
|
|
27
|
-
},
|
|
18
|
+
".": "./dist/index.js",
|
|
28
19
|
"./package.json": "./package.json"
|
|
29
20
|
},
|
|
30
21
|
"types": "./dist/index.d.ts",
|
|
@@ -32,35 +23,28 @@
|
|
|
32
23
|
"dist"
|
|
33
24
|
],
|
|
34
25
|
"sideEffects": false,
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"vue": "^3.0.0"
|
|
31
|
+
},
|
|
35
32
|
"devDependencies": {
|
|
36
|
-
"@
|
|
37
|
-
"@
|
|
38
|
-
"@
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"tsdown": "^0.18.1",
|
|
47
|
-
"type-fest": "^5.4.1",
|
|
48
|
-
"typescript": "^5.9.3",
|
|
49
|
-
"unplugin-vue": "^7.1.0",
|
|
50
|
-
"vite": "^7.3.0",
|
|
51
|
-
"vitest": "^4.0.16",
|
|
52
|
-
"vitest-browser-vue": "^2.0.1",
|
|
53
|
-
"vue": "^3.5.25",
|
|
54
|
-
"vue-tsc": "^3.2.2"
|
|
33
|
+
"@types/node": "^25.5.0",
|
|
34
|
+
"@vitejs/plugin-vue": "^6.0.5",
|
|
35
|
+
"@vue/test-utils": "^2.4.6",
|
|
36
|
+
"happy-dom": "^20.8.9",
|
|
37
|
+
"tsdown": "^0.21.7",
|
|
38
|
+
"type-fest": "^5.5.0",
|
|
39
|
+
"vite": "^8.0.3",
|
|
40
|
+
"vitest": "^4.1.2",
|
|
41
|
+
"vue": "^3.5.31",
|
|
42
|
+
"vue-tsc": "^3.2.6"
|
|
55
43
|
},
|
|
56
44
|
"dependencies": {
|
|
57
45
|
"@standard-schema/spec": "^1.1.0",
|
|
58
|
-
"dot-prop": "^10.1.0",
|
|
59
46
|
"es-toolkit": "^1.45.1"
|
|
60
47
|
},
|
|
61
|
-
"engines": {
|
|
62
|
-
"node": ">=24.0.0"
|
|
63
|
-
},
|
|
64
48
|
"keywords": [
|
|
65
49
|
"notform",
|
|
66
50
|
"vue",
|
|
@@ -69,13 +53,18 @@
|
|
|
69
53
|
"validator",
|
|
70
54
|
"vue form validator"
|
|
71
55
|
],
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=24.13.0"
|
|
58
|
+
},
|
|
59
|
+
"inlinedDependencies": {
|
|
60
|
+
"type-fest": "5.5.0"
|
|
61
|
+
},
|
|
72
62
|
"scripts": {
|
|
73
63
|
"build": "tsdown",
|
|
74
64
|
"dev": "tsdown --watch",
|
|
75
65
|
"test": "vitest",
|
|
76
66
|
"test:watch": "vitest watch --ui",
|
|
77
67
|
"typecheck": "vue-tsc --noEmit",
|
|
78
|
-
"lint": "eslint . --fix"
|
|
79
|
-
"cleanup": "rm -rf dist node_modules .turbo package-lock.json"
|
|
68
|
+
"lint": "eslint . --fix"
|
|
80
69
|
}
|
|
81
70
|
}
|