@hybridly/vue 0.10.0-beta.18 → 0.10.0-beta.19
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.mts +28 -11
- package/dist/index.mjs +86 -12
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -31,7 +31,8 @@ declare const Deferred: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
31
31
|
//#endregion
|
|
32
32
|
//#region src/composables/form.d.ts
|
|
33
33
|
type Errors$1<T extends SearchableObject$1> = { [K in keyof T]?: T[K] extends Record<string, any> ? Errors$1<T[K]> : string };
|
|
34
|
-
type
|
|
34
|
+
type FormFieldBehavior$1 = boolean | string[];
|
|
35
|
+
type DefaultFormOptions = Pick<FormOptions<object>, 'timeout' | 'resetOnSuccess' | 'resetOnError' | 'setDefaultOnSuccess' | 'progress' | 'preserveScroll' | 'preserveState' | 'preserveUrl' | 'headers' | 'errorBag' | 'spoof' | 'transformUrl' | 'updateHistoryState' | 'useFormData'>;
|
|
35
36
|
interface FormOptions<T extends SearchableObject$1> extends Omit<HybridRequestOptions, 'data' | 'url' | 'reset'> {
|
|
36
37
|
fields: T;
|
|
37
38
|
url?: UrlResolvable | (() => UrlResolvable);
|
|
@@ -44,12 +45,17 @@ interface FormOptions<T extends SearchableObject$1> extends Omit<HybridRequestOp
|
|
|
44
45
|
* Resets the fields of the form to their default value after a successful submission.
|
|
45
46
|
* @default true
|
|
46
47
|
*/
|
|
47
|
-
resetOnSuccess?:
|
|
48
|
+
resetOnSuccess?: FormFieldBehavior$1;
|
|
49
|
+
/**
|
|
50
|
+
* Resets the fields of the form to their default value after a failed submission.
|
|
51
|
+
* @default false
|
|
52
|
+
*/
|
|
53
|
+
resetOnError?: FormFieldBehavior$1;
|
|
48
54
|
/**
|
|
49
55
|
* Updates the default values from the form after a successful submission.
|
|
50
56
|
* @default false
|
|
51
57
|
*/
|
|
52
|
-
setDefaultOnSuccess?:
|
|
58
|
+
setDefaultOnSuccess?: FormFieldBehavior$1;
|
|
53
59
|
/**
|
|
54
60
|
* Callback executed before the form submission for transforming the fields.
|
|
55
61
|
*/
|
|
@@ -85,6 +91,7 @@ declare function useForm<T extends SearchableObject$1, P extends Path$1<T> & str
|
|
|
85
91
|
//#region src/components/form.d.ts
|
|
86
92
|
type DefaultFormFields = Record<string, any>;
|
|
87
93
|
type FormInternalSubmitOptions<T extends SearchableObject$1 = DefaultFormFields> = NonNullable<Parameters<FormReturn<T>['submit']>[0]>;
|
|
94
|
+
type FormFieldBehavior = boolean | string[];
|
|
88
95
|
type FormRequestOptions = Omit<HybridRequestOptions, 'url' | 'data' | 'method' | 'errorBag' | 'progress'>;
|
|
89
96
|
type FormSubmitOptions<T extends SearchableObject$1 = DefaultFormFields> = FormInternalSubmitOptions<T>;
|
|
90
97
|
type FormSlotProps<T extends SearchableObject$1 = DefaultFormFields> = Omit<FormReturn<T>, 'submit' | 'reset' | 'resetFields' | 'errors'> & {
|
|
@@ -101,8 +108,9 @@ interface FormProps {
|
|
|
101
108
|
errorBag?: string;
|
|
102
109
|
showProgress?: boolean;
|
|
103
110
|
disableWhileProcessing?: boolean;
|
|
104
|
-
resetOnSuccess?:
|
|
105
|
-
|
|
111
|
+
resetOnSuccess?: FormFieldBehavior;
|
|
112
|
+
resetOnError?: FormFieldBehavior;
|
|
113
|
+
setDefaultOnSuccess?: FormFieldBehavior;
|
|
106
114
|
}
|
|
107
115
|
declare const Form: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
108
116
|
action: {
|
|
@@ -133,11 +141,15 @@ declare const Form: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
133
141
|
default: false;
|
|
134
142
|
};
|
|
135
143
|
resetOnSuccess: {
|
|
136
|
-
type:
|
|
144
|
+
type: PropType<FormFieldBehavior>;
|
|
137
145
|
default: true;
|
|
138
146
|
};
|
|
147
|
+
resetOnError: {
|
|
148
|
+
type: PropType<FormFieldBehavior>;
|
|
149
|
+
default: false;
|
|
150
|
+
};
|
|
139
151
|
setDefaultOnSuccess: {
|
|
140
|
-
type:
|
|
152
|
+
type: PropType<FormFieldBehavior>;
|
|
141
153
|
default: false;
|
|
142
154
|
};
|
|
143
155
|
}>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
|
|
@@ -171,11 +183,15 @@ declare const Form: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
171
183
|
default: false;
|
|
172
184
|
};
|
|
173
185
|
resetOnSuccess: {
|
|
174
|
-
type:
|
|
186
|
+
type: PropType<FormFieldBehavior>;
|
|
175
187
|
default: true;
|
|
176
188
|
};
|
|
189
|
+
resetOnError: {
|
|
190
|
+
type: PropType<FormFieldBehavior>;
|
|
191
|
+
default: false;
|
|
192
|
+
};
|
|
177
193
|
setDefaultOnSuccess: {
|
|
178
|
-
type:
|
|
194
|
+
type: PropType<FormFieldBehavior>;
|
|
179
195
|
default: false;
|
|
180
196
|
};
|
|
181
197
|
}>> & Readonly<{}>, {
|
|
@@ -185,8 +201,9 @@ declare const Form: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
185
201
|
action: string;
|
|
186
202
|
showProgress: boolean;
|
|
187
203
|
disableWhileProcessing: boolean;
|
|
188
|
-
resetOnSuccess:
|
|
189
|
-
|
|
204
|
+
resetOnSuccess: FormFieldBehavior;
|
|
205
|
+
resetOnError: FormFieldBehavior;
|
|
206
|
+
setDefaultOnSuccess: FormFieldBehavior;
|
|
190
207
|
}, SlotsType<{
|
|
191
208
|
default: FormSlotProps;
|
|
192
209
|
}>, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
package/dist/index.mjs
CHANGED
|
@@ -153,9 +153,27 @@ function useForm(options) {
|
|
|
153
153
|
*/
|
|
154
154
|
function setDefault(newDefault) {
|
|
155
155
|
Object.entries(newDefault).forEach(([key, value]) => {
|
|
156
|
-
|
|
156
|
+
set(defaults, key, safeClone(value));
|
|
157
157
|
});
|
|
158
158
|
}
|
|
159
|
+
function resolveFieldBehaviorKeys(option, fallback) {
|
|
160
|
+
if (option === false) return;
|
|
161
|
+
if (Array.isArray(option)) return option.length > 0 ? option : void 0;
|
|
162
|
+
if (option === true) return Object.keys(fields);
|
|
163
|
+
return fallback ? Object.keys(fields) : void 0;
|
|
164
|
+
}
|
|
165
|
+
function setDefaultFromFields(option) {
|
|
166
|
+
const keys = resolveFieldBehaviorKeys(option, false);
|
|
167
|
+
if (!keys) return;
|
|
168
|
+
keys.forEach((key) => {
|
|
169
|
+
set(defaults, key, safeClone(get(fields, key)));
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function resetFieldsFromBehavior(option, fallback) {
|
|
173
|
+
const keys = resolveFieldBehaviorKeys(option, fallback);
|
|
174
|
+
if (!keys) return;
|
|
175
|
+
resetFields(...keys);
|
|
176
|
+
}
|
|
159
177
|
/**
|
|
160
178
|
* Resets the form's failed and successful flags.
|
|
161
179
|
*/
|
|
@@ -200,7 +218,7 @@ function useForm(options) {
|
|
|
200
218
|
function submit(optionsOverrides) {
|
|
201
219
|
const { fields: _f, key: _k, ...optionsWithoutFields } = options;
|
|
202
220
|
const resolvedOptions = optionsOverrides ? merge(optionsWithoutFields, optionsOverrides, { mergePlainObjects: true }) : optionsWithoutFields;
|
|
203
|
-
const { timeout, resetOnSuccess, setDefaultOnSuccess, transform, ...requestOptions } = merge(formStore.getDefaultConfig(), resolvedOptions, { mergePlainObjects: true });
|
|
221
|
+
const { timeout, resetOnSuccess, resetOnError, setDefaultOnSuccess, transform, ...requestOptions } = merge(formStore.getDefaultConfig(), resolvedOptions, { mergePlainObjects: true });
|
|
204
222
|
const url = typeof requestOptions.url === "function" ? requestOptions.url() : requestOptions.url;
|
|
205
223
|
const data = typeof transform === "function" ? transform(fields) : fields;
|
|
206
224
|
const preserveState = requestOptions.preserveState ?? requestOptions.method !== "GET";
|
|
@@ -232,6 +250,7 @@ function useForm(options) {
|
|
|
232
250
|
},
|
|
233
251
|
"validation-error": (incoming, request, context) => {
|
|
234
252
|
setErrors(incoming);
|
|
253
|
+
resetFieldsFromBehavior(resetOnError, false);
|
|
235
254
|
failed.value = true;
|
|
236
255
|
recentlyFailed.value = true;
|
|
237
256
|
timeoutIds.recentlyFailed = setTimeout(() => recentlyFailed.value = false, timeout ?? 5e3);
|
|
@@ -239,8 +258,8 @@ function useForm(options) {
|
|
|
239
258
|
},
|
|
240
259
|
success: (payload, request, response, context) => {
|
|
241
260
|
clearErrors();
|
|
242
|
-
|
|
243
|
-
|
|
261
|
+
setDefaultFromFields(setDefaultOnSuccess);
|
|
262
|
+
resetFieldsFromBehavior(resetOnSuccess, true);
|
|
244
263
|
successful.value = true;
|
|
245
264
|
recentlySuccessful.value = true;
|
|
246
265
|
timeoutIds.recentlySuccessful = setTimeout(() => recentlySuccessful.value = false, timeout ?? 5e3);
|
|
@@ -371,8 +390,10 @@ function getControlFieldInfo(control) {
|
|
|
371
390
|
appendToArray
|
|
372
391
|
};
|
|
373
392
|
}
|
|
374
|
-
function setCurrentValuesAsDefaults(form) {
|
|
393
|
+
function setCurrentValuesAsDefaults(form, keys) {
|
|
375
394
|
for (const element of Array.from(form.elements)) {
|
|
395
|
+
const field = getControlFieldInfo(element);
|
|
396
|
+
if (keys && (!field || !keys.has(field.path))) continue;
|
|
376
397
|
if (element instanceof HTMLInputElement) {
|
|
377
398
|
if (element.type === "checkbox" || element.type === "radio") {
|
|
378
399
|
element.defaultChecked = element.checked;
|
|
@@ -490,11 +511,15 @@ const Form = defineComponent({
|
|
|
490
511
|
default: false
|
|
491
512
|
},
|
|
492
513
|
resetOnSuccess: {
|
|
493
|
-
type: Boolean,
|
|
514
|
+
type: [Boolean, Array],
|
|
494
515
|
default: true
|
|
495
516
|
},
|
|
517
|
+
resetOnError: {
|
|
518
|
+
type: [Boolean, Array],
|
|
519
|
+
default: false
|
|
520
|
+
},
|
|
496
521
|
setDefaultOnSuccess: {
|
|
497
|
-
type: Boolean,
|
|
522
|
+
type: [Boolean, Array],
|
|
498
523
|
default: false
|
|
499
524
|
}
|
|
500
525
|
},
|
|
@@ -508,6 +533,7 @@ const Form = defineComponent({
|
|
|
508
533
|
errorBag: props.errorBag,
|
|
509
534
|
progress: props.showProgress,
|
|
510
535
|
resetOnSuccess: props.resetOnSuccess,
|
|
536
|
+
resetOnError: props.resetOnError,
|
|
511
537
|
setDefaultOnSuccess: props.setDefaultOnSuccess
|
|
512
538
|
});
|
|
513
539
|
function collectFormFields(submitter) {
|
|
@@ -521,6 +547,47 @@ const Form = defineComponent({
|
|
|
521
547
|
}
|
|
522
548
|
return fields;
|
|
523
549
|
}
|
|
550
|
+
function collectDefaultFormFields() {
|
|
551
|
+
if (!element.value) return {};
|
|
552
|
+
const fields = {};
|
|
553
|
+
for (const control of Array.from(element.value.elements)) {
|
|
554
|
+
const field = getControlFieldInfo(control);
|
|
555
|
+
if (!field) continue;
|
|
556
|
+
if (control instanceof HTMLInputElement) {
|
|
557
|
+
if (control.type === "file") continue;
|
|
558
|
+
if (control.type === "checkbox" || control.type === "radio") {
|
|
559
|
+
if (control.defaultChecked) appendFieldValue(fields, field.path, control.value, field.appendToArray);
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
appendFieldValue(fields, field.path, control.defaultValue, field.appendToArray);
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (control instanceof HTMLTextAreaElement) {
|
|
566
|
+
appendFieldValue(fields, field.path, control.defaultValue, field.appendToArray);
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
if (control instanceof HTMLSelectElement) {
|
|
570
|
+
if (control.multiple) {
|
|
571
|
+
for (const option of Array.from(control.options)) if (option.defaultSelected) appendFieldValue(fields, field.path, option.value, field.appendToArray);
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
const defaultOption = Array.from(control.options).find((option) => option.defaultSelected) ?? control.options.item(0);
|
|
575
|
+
if (defaultOption) appendFieldValue(fields, field.path, defaultOption.value, field.appendToArray);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return fields;
|
|
579
|
+
}
|
|
580
|
+
function syncDefaultsFromNativeControls() {
|
|
581
|
+
if (!element.value) return;
|
|
582
|
+
const defaultFields = collectDefaultFormFields();
|
|
583
|
+
const nextDefaults = {};
|
|
584
|
+
for (const control of Array.from(element.value.elements)) {
|
|
585
|
+
const field = getControlFieldInfo(control);
|
|
586
|
+
if (!field) continue;
|
|
587
|
+
nextDefaults[field.path] = get(defaultFields, field.path);
|
|
588
|
+
}
|
|
589
|
+
form.setDefault(nextDefaults);
|
|
590
|
+
}
|
|
524
591
|
function syncFields(fields) {
|
|
525
592
|
const target = form.fields;
|
|
526
593
|
Object.keys(target).forEach((key) => {
|
|
@@ -550,16 +617,17 @@ const Form = defineComponent({
|
|
|
550
617
|
}
|
|
551
618
|
function submit(options, submitter) {
|
|
552
619
|
syncFields(collectFormFields(submitter));
|
|
553
|
-
|
|
620
|
+
syncDefaultsFromNativeControls();
|
|
621
|
+
const { hooks, ...rest } = merge({
|
|
554
622
|
errorBag: props.errorBag,
|
|
555
623
|
progress: props.showProgress,
|
|
556
624
|
resetOnSuccess: props.resetOnSuccess,
|
|
625
|
+
resetOnError: props.resetOnError,
|
|
557
626
|
setDefaultOnSuccess: props.setDefaultOnSuccess
|
|
558
627
|
}, {
|
|
559
628
|
...props.options,
|
|
560
629
|
...options
|
|
561
630
|
}, { mergePlainObjects: true });
|
|
562
|
-
const { hooks, ...rest } = resolvedOptions;
|
|
563
631
|
const submitOptions = {
|
|
564
632
|
...rest,
|
|
565
633
|
hooks: {
|
|
@@ -572,9 +640,15 @@ const Form = defineComponent({
|
|
|
572
640
|
return hooks?.["validation-error"]?.(incoming, request, context);
|
|
573
641
|
},
|
|
574
642
|
success: (payload, request, response, context) => {
|
|
575
|
-
if (element.value &&
|
|
576
|
-
|
|
577
|
-
|
|
643
|
+
if (element.value && rest.setDefaultOnSuccess !== false) {
|
|
644
|
+
const selectedKeys = Array.isArray(rest.setDefaultOnSuccess) ? new Set(rest.setDefaultOnSuccess) : void 0;
|
|
645
|
+
setCurrentValuesAsDefaults(element.value, selectedKeys);
|
|
646
|
+
syncDefaultsFromNativeControls();
|
|
647
|
+
}
|
|
648
|
+
nextTick(() => {
|
|
649
|
+
if (!element.value) return;
|
|
650
|
+
syncControlsFromFields(element.value, form.fields);
|
|
651
|
+
});
|
|
578
652
|
return hooks?.success?.(payload, request, response, context);
|
|
579
653
|
}
|
|
580
654
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hybridly/vue",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.10.0-beta.
|
|
4
|
+
"version": "0.10.0-beta.19",
|
|
5
5
|
"description": "Vue adapter for Hybridly",
|
|
6
6
|
"author": "Enzo Innocenzi <enzo@innocenzi.dev>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@clickbar/dot-diver": "^1.0.7",
|
|
49
49
|
"es-toolkit": "^1.45.1",
|
|
50
|
-
"@hybridly/core": "0.10.0-beta.
|
|
51
|
-
"@hybridly/utils": "0.10.0-beta.
|
|
50
|
+
"@hybridly/core": "0.10.0-beta.19",
|
|
51
|
+
"@hybridly/utils": "0.10.0-beta.19",
|
|
52
52
|
"@vue/devtools-api": "^8.1.0",
|
|
53
53
|
"defu": "^6.1.4",
|
|
54
54
|
"nprogress": "^0.2.0"
|