@teamnovu/nuxt-statamic-formbuilder 1.1.0 → 1.3.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/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.16.0"
6
6
  },
7
- "version": "1.1.0",
7
+ "version": "1.3.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
@@ -5,6 +5,7 @@ type __VLS_Props = {
5
5
  serverUrl?: string;
6
6
  styleConfig?: FormBuilderStyleConfig;
7
7
  submitter?: FormBuilderSubmitter;
8
+ resetDelay?: number | false;
8
9
  /**
9
10
  * Override the FormBuilder component (defaults to globally registered `FormBuilder`
10
11
  * from a UI layer such as `@teamnovu/nuxt-statamic-formbuilder-nuxt-ui`).
@@ -7,6 +7,7 @@
7
7
  :texts="texts"
8
8
  :style-config="styleConfig"
9
9
  :submitter="submitter"
10
+ :reset-delay="resetDelay"
10
11
  >
11
12
  <template
12
13
  v-for="(_, name) in $slots"
@@ -30,6 +31,7 @@ const props = defineProps({
30
31
  serverUrl: { type: String, required: false },
31
32
  styleConfig: { type: Object, required: false },
32
33
  submitter: { type: Function, required: false },
34
+ resetDelay: { type: [Number, Boolean], required: false },
33
35
  formComponent: { type: null, required: false }
34
36
  });
35
37
  const runtimeConfig = useRuntimeConfig();
@@ -5,6 +5,7 @@ type __VLS_Props = {
5
5
  serverUrl?: string;
6
6
  styleConfig?: FormBuilderStyleConfig;
7
7
  submitter?: FormBuilderSubmitter;
8
+ resetDelay?: number | false;
8
9
  /**
9
10
  * Override the FormBuilder component (defaults to globally registered `FormBuilder`
10
11
  * from a UI layer such as `@teamnovu/nuxt-statamic-formbuilder-nuxt-ui`).
@@ -2,6 +2,7 @@ import { useRuntimeConfig } from "#imports";
2
2
  import { useI18n } from "vue-i18n";
3
3
  import { computed, ref, toValue } from "vue";
4
4
  import { isDisplayOrSpacerField, isMultiValueField } from "../../types.js";
5
+ import { pushFormSubmissionDataLayer } from "../../utils/pushFormSubmissionDataLayer.js";
5
6
  import { submitStatamicForm } from "../../utils/submitStatamicForm.js";
6
7
  function flattenFields(form) {
7
8
  if ("fields" in form) {
@@ -18,6 +19,8 @@ function setInitialState(fields) {
18
19
  acc[field.handle] = isMultiValueField(field) ? [] : null;
19
20
  } else if (isMultiValueField(field)) {
20
21
  acc[field.handle] = [];
22
+ } else if (field.type === "input_switch") {
23
+ acc[field.handle] = field.config.default === true;
21
24
  } else {
22
25
  acc[field.handle] = void 0;
23
26
  }
@@ -50,6 +53,7 @@ export function useFormBuilder(formBuilderConfiguration, serverUrlOrOptions) {
50
53
  locale: locale.value
51
54
  });
52
55
  requestState.value = "success";
56
+ pushFormSubmissionDataLayer(formBuilderConfiguration);
53
57
  return result;
54
58
  } catch (error) {
55
59
  requestState.value = "error";
@@ -19,6 +19,9 @@ export function useFormValidation(fieldsInput, formRules, _state) {
19
19
  if (name === "required") {
20
20
  return { ...parsedRules, required: true };
21
21
  }
22
+ if (name === "accepted") {
23
+ return { ...parsedRules, accepted: true };
24
+ }
22
25
  if (name === "min" && value) {
23
26
  return { ...parsedRules, min: Number(value) };
24
27
  }
@@ -60,7 +63,7 @@ export function useFormValidation(fieldsInput, formRules, _state) {
60
63
  const buildFieldSchema = (field) => {
61
64
  const rules = getRulesForField(field);
62
65
  const normalizedType = normalizeFieldType(field, rules);
63
- const isRequired = rules.required === true;
66
+ const isRequired = rules.required === true || rules.accepted === true;
64
67
  const buildStringSchema = () => {
65
68
  let stringSchema2 = v.union([
66
69
  v.pipe(v.string()),
@@ -126,7 +129,7 @@ export function useFormValidation(fieldsInput, formRules, _state) {
126
129
  return arraySchema();
127
130
  }
128
131
  if (normalizedType === "boolean") {
129
- const base = v.boolean();
132
+ const base = rules.accepted === true ? v.pipe(v.boolean(), v.check((value) => value === true, t("form.user_feedback.field_validation.required"))) : v.boolean();
130
133
  return isRequired ? base : v.optional(base);
131
134
  }
132
135
  if (normalizedType === "file" && field.type === "input_file_upload") {
@@ -71,6 +71,7 @@ export declare function isFileUploadField(field: FormBuilderField): field is Ext
71
71
  export declare function isMultiValueField(field: FormBuilderField): boolean;
72
72
  export type InputRules = {
73
73
  required?: boolean;
74
+ accepted?: boolean;
74
75
  min?: number;
75
76
  max?: number;
76
77
  maxlength?: number;
@@ -92,6 +93,8 @@ export type DefaultInputField = {
92
93
  export type defaultInputField = DefaultInputField;
93
94
  export type FormBuilderConfiguration = {
94
95
  handle: string;
96
+ /** CMS form title (Statamic GraphQL `title`). Used for analytics. */
97
+ title?: string | null;
95
98
  honeypot: string | null;
96
99
  rules: {
97
100
  [key: string]: string;
@@ -0,0 +1,11 @@
1
+ import type { FormBuilderConfiguration } from '../types.js';
2
+ export type FormSubmissionDataLayerEvent = {
3
+ event: 'form_submission';
4
+ form_id: string;
5
+ form_name: string;
6
+ };
7
+ /**
8
+ * Push a GTM-compatible form_submission event after a successful submit.
9
+ * No-op on the server and when `window` is unavailable.
10
+ */
11
+ export declare function pushFormSubmissionDataLayer(form: FormBuilderConfiguration): void;
@@ -0,0 +1,16 @@
1
+ function getDataLayer() {
2
+ const win = window;
3
+ win.dataLayer ??= [];
4
+ return win.dataLayer;
5
+ }
6
+ export function pushFormSubmissionDataLayer(form) {
7
+ if (typeof window === "undefined") {
8
+ return;
9
+ }
10
+ const event = {
11
+ event: "form_submission",
12
+ form_id: form.handle,
13
+ form_name: form.title ?? ""
14
+ };
15
+ getDataLayer().push(event);
16
+ }
@@ -24,23 +24,25 @@ export async function submitStatamicForm(args) {
24
24
  }
25
25
  const formData = new FormData();
26
26
  formData.append("_site", locale);
27
+ const fields = flattenFields(form);
27
28
  Object.entries(state).forEach(([key, value]) => {
28
- if (value?.[0]?.size || value?.[1]?.size) {
29
- for (let x = 0; x < value.length; x++) {
30
- formData.append(`${key}[${x}]`, value[x]);
29
+ if (Array.isArray(value)) {
30
+ const field = fields.find((candidate) => candidate.handle === key);
31
+ if (field?.type === "input_radio_buttons") {
32
+ if (value[0] != null) {
33
+ formData.append(key, value[0]);
34
+ }
35
+ } else {
36
+ for (let x = 0; x < value.length; x++) {
37
+ formData.append(`${key}[${x}]`, value[x]);
38
+ }
31
39
  }
40
+ } else if (typeof value === "boolean") {
41
+ formData.append(key, value ? "1" : "0");
32
42
  } else if (value != null) {
33
43
  formData.append(key, value);
34
44
  }
35
45
  });
36
- flattenFields(form).filter((field) => ["checkboxes", "input_checkboxes"].includes(field.type)).forEach((field) => {
37
- const copy = [...formData.entries().filter(([key]) => key?.startsWith("["))];
38
- copy.forEach(([key, value]) => {
39
- formData.delete(key);
40
- const newKey = key.replace(`[${field.handle}]`, field.handle);
41
- formData.append(newKey, value);
42
- });
43
- });
44
46
  const response = await fetch(
45
47
  `${serverUrl}/!/forms/${form.handle}`,
46
48
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamnovu/nuxt-statamic-formbuilder",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Headless Nuxt form builder with Statamic submit adapter",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/teamnovu/nuxt-statamic-formbuilder#readme",