@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 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 DefaultFormOptions = Pick<FormOptions<object>, 'timeout' | 'resetOnSuccess' | 'setDefaultOnSuccess' | 'progress' | 'preserveScroll' | 'preserveState' | 'preserveUrl' | 'headers' | 'errorBag' | 'spoof' | 'transformUrl' | 'updateHistoryState' | 'useFormData'>;
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?: boolean;
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?: boolean;
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?: boolean;
105
- setDefaultOnSuccess?: boolean;
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: BooleanConstructor;
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: BooleanConstructor;
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: BooleanConstructor;
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: BooleanConstructor;
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: boolean;
189
- setDefaultOnSuccess: boolean;
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
- Reflect.set(defaults, key, safeClone(value));
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
- if (setDefaultOnSuccess) setDefault(fields);
243
- if (resetOnSuccess !== false) resetFields();
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
- const resolvedOptions = merge({
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 && resolvedOptions.setDefaultOnSuccess) setCurrentValuesAsDefaults(element.value);
576
- if (resolvedOptions.resetOnSuccess ?? props.resetOnSuccess) resetFields();
577
- syncFields(collectFormFields());
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.18",
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.18",
51
- "@hybridly/utils": "0.10.0-beta.18",
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"