@teamnovu/nuxt-statamic-formbuilder 1.4.1 → 1.4.3
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 +1 -1
- package/dist/runtime/app/components/StatamicFormBuilderFields.vue +5 -1
- package/dist/runtime/app/composables/useFieldConditions.js +39 -6
- package/dist/runtime/app/composables/useFormValidation.d.ts +1 -1
- package/dist/runtime/app/composables/useFormValidation.js +12 -2
- package/dist/runtime/types.d.ts +1 -1
- package/dist/runtime/utils/fieldHasRequiredRule.d.ts +1 -0
- package/dist/runtime/utils/fieldHasRequiredRule.js +23 -0
- package/package.json +5 -2
package/dist/module.json
CHANGED
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
<script setup>
|
|
37
37
|
import { inject, ref } from "vue";
|
|
38
38
|
import { formBuilderStateKey } from "../../injectionKeys";
|
|
39
|
+
import { fieldHasRequiredRule } from "../../utils/fieldHasRequiredRule";
|
|
39
40
|
import { useFieldConditions } from "../composables/useFieldConditions";
|
|
40
41
|
import { resolveFormField } from "../composables/useFormFieldRegistry";
|
|
41
42
|
const props = defineProps({
|
|
@@ -59,7 +60,10 @@ const slotBindings = (field) => ({
|
|
|
59
60
|
id: field.handle,
|
|
60
61
|
ariaLabel: field.display ?? field.handle,
|
|
61
62
|
rules: {
|
|
62
|
-
required:
|
|
63
|
+
required: isVisible(field) && fieldHasRequiredRule(
|
|
64
|
+
props.form?.rules?.[field.handle],
|
|
65
|
+
state.value
|
|
66
|
+
)
|
|
63
67
|
}
|
|
64
68
|
},
|
|
65
69
|
error: props.validationErrors?.[field.handle] ?? props.error?.[field.handle] ?? null
|
|
@@ -105,6 +105,32 @@ function resolveField(field, formValues) {
|
|
|
105
105
|
}
|
|
106
106
|
return dataGet(formValues, field);
|
|
107
107
|
}
|
|
108
|
+
function normalizeScalar(value) {
|
|
109
|
+
if (typeof value !== "string") return value;
|
|
110
|
+
const trimmed = value.trim();
|
|
111
|
+
return trimmed === "" ? null : trimmed;
|
|
112
|
+
}
|
|
113
|
+
function compareScalarAgainstArray(lhs, rhs, op) {
|
|
114
|
+
const rhsValue = normalizeScalar(rhs);
|
|
115
|
+
const comparePair = (left, right) => {
|
|
116
|
+
switch (op) {
|
|
117
|
+
// eslint-disable-next-line eqeqeq
|
|
118
|
+
case "==":
|
|
119
|
+
return normalizeScalar(left) == right;
|
|
120
|
+
// eslint-disable-next-line eqeqeq
|
|
121
|
+
case "!=":
|
|
122
|
+
return normalizeScalar(left) != right;
|
|
123
|
+
case "===":
|
|
124
|
+
return normalizeScalar(left) === right;
|
|
125
|
+
case "!==":
|
|
126
|
+
return normalizeScalar(left) !== right;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
if (op === "==" || op === "===") {
|
|
130
|
+
return lhs.some((value) => comparePair(value, rhsValue));
|
|
131
|
+
}
|
|
132
|
+
return lhs.every((value) => comparePair(value, rhsValue));
|
|
133
|
+
}
|
|
108
134
|
function testCondition(condition, formValues) {
|
|
109
135
|
const op = normalizeOperator(condition.operator);
|
|
110
136
|
const rawLhs = resolveField(condition.field, formValues);
|
|
@@ -164,13 +190,20 @@ function testCondition(condition, formValues) {
|
|
|
164
190
|
return lhsNum <= rhsNum;
|
|
165
191
|
}
|
|
166
192
|
}
|
|
167
|
-
if (rawLhs
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
193
|
+
if (Array.isArray(rawLhs)) {
|
|
194
|
+
switch (op) {
|
|
195
|
+
case "==":
|
|
196
|
+
case "!=":
|
|
197
|
+
case "===":
|
|
198
|
+
case "!==":
|
|
199
|
+
return compareScalarAgainstArray(rawLhs, rhs, op);
|
|
200
|
+
default:
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
172
203
|
}
|
|
173
|
-
if (typeof
|
|
204
|
+
if (rawLhs !== null && typeof rawLhs === "object") return false;
|
|
205
|
+
const lhs = normalizeScalar(rawLhs);
|
|
206
|
+
rhs = normalizeScalar(rhs);
|
|
174
207
|
switch (op) {
|
|
175
208
|
// Loose equality intentional — mirrors the original eval() behaviour,
|
|
176
209
|
// e.g. null == "null" should be false but null == null should be true.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as v from 'valibot';
|
|
2
2
|
import type { MaybeRefOrGetter, Ref } from 'vue';
|
|
3
3
|
import type { FormBuilderConfiguration, FormBuilderField, InputRules } from '../../types.js';
|
|
4
|
-
export declare function useFormValidation(fieldsInput: MaybeRefOrGetter<readonly FormBuilderField[]>, formRules: FormBuilderConfiguration['rules'],
|
|
4
|
+
export declare function useFormValidation(fieldsInput: MaybeRefOrGetter<readonly FormBuilderField[]>, formRules: FormBuilderConfiguration['rules'], stateInput?: Ref<Record<string, unknown>>): {
|
|
5
5
|
schema: import("vue").ComputedRef<v.ObjectSchema<Record<string, v.BaseSchema<unknown, unknown, v.BaseIssue<unknown>>>, undefined>>;
|
|
6
6
|
validationErrors: Ref<Record<string, string>, Record<string, string>>;
|
|
7
7
|
parseRules: (rulesInput?: string | string[]) => InputRules;
|
|
@@ -2,10 +2,17 @@ import * as v from "valibot";
|
|
|
2
2
|
import { useI18n } from "vue-i18n";
|
|
3
3
|
import { computed, ref, toValue } from "vue";
|
|
4
4
|
import { isDisplayOrSpacerField } from "../../types.js";
|
|
5
|
-
|
|
5
|
+
import { fieldHasRequiredRule } from "../../utils/fieldHasRequiredRule.js";
|
|
6
|
+
import { useFieldConditions } from "./useFieldConditions.js";
|
|
7
|
+
export function useFormValidation(fieldsInput, formRules, stateInput) {
|
|
6
8
|
const { t } = useI18n();
|
|
7
9
|
const fields = computed(() => toValue(fieldsInput) ?? []);
|
|
8
10
|
const validationErrors = ref({});
|
|
11
|
+
const formState = computed(() => toValue(stateInput) ?? {});
|
|
12
|
+
const { visibilityMap } = useFieldConditions(
|
|
13
|
+
() => [...fields.value],
|
|
14
|
+
formState
|
|
15
|
+
);
|
|
9
16
|
const parseRules = (rulesInput) => {
|
|
10
17
|
if (!rulesInput) {
|
|
11
18
|
return {};
|
|
@@ -63,7 +70,7 @@ export function useFormValidation(fieldsInput, formRules, _state) {
|
|
|
63
70
|
const buildFieldSchema = (field) => {
|
|
64
71
|
const rules = getRulesForField(field);
|
|
65
72
|
const normalizedType = normalizeFieldType(field, rules);
|
|
66
|
-
const isRequired = rules.required === true || rules.accepted === true;
|
|
73
|
+
const isRequired = rules.required === true || rules.accepted === true || fieldHasRequiredRule(formRules?.[field.handle], formState.value);
|
|
67
74
|
const buildStringSchema = () => {
|
|
68
75
|
let stringSchema2 = v.union([
|
|
69
76
|
v.pipe(v.string()),
|
|
@@ -154,6 +161,9 @@ export function useFormValidation(fieldsInput, formRules, _state) {
|
|
|
154
161
|
if (isDisplayOrSpacerField(field)) {
|
|
155
162
|
return;
|
|
156
163
|
}
|
|
164
|
+
if (stateInput && !visibilityMap.value[field.handle]) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
157
167
|
schemaEntries[field.handle] = buildFieldSchema(field);
|
|
158
168
|
});
|
|
159
169
|
return v.object(schemaEntries);
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function fieldHasRequiredRule(rules?: string | string[], state?: Record<string, unknown>): boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function fieldHasRequiredRule(rules, state = {}) {
|
|
2
|
+
if (!rules) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
const ruleList = Array.isArray(rules) ? rules : rules.split("|");
|
|
6
|
+
return ruleList.some((rule) => {
|
|
7
|
+
if (rule === "required") {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
if (!rule.startsWith("required_if:")) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const [field, ...values] = rule.slice("required_if:".length).split(",");
|
|
14
|
+
if (!field || values.length === 0) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const fieldValue = state[field];
|
|
18
|
+
if (Array.isArray(fieldValue)) {
|
|
19
|
+
return fieldValue.some((value) => values.includes(String(value)));
|
|
20
|
+
}
|
|
21
|
+
return values.includes(String(fieldValue ?? ""));
|
|
22
|
+
});
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamnovu/nuxt-statamic-formbuilder",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
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",
|
|
@@ -80,12 +80,15 @@
|
|
|
80
80
|
"eslint": "^10.0.2",
|
|
81
81
|
"nuxt": "^4.3.1",
|
|
82
82
|
"typescript": "^5.9.3",
|
|
83
|
+
"vitest": "^5.0.1",
|
|
83
84
|
"vue": "^3.5.13",
|
|
84
85
|
"vue-i18n": "^11.4.6",
|
|
85
86
|
"vue-tsc": "^3.3.7"
|
|
86
87
|
},
|
|
87
88
|
"scripts": {
|
|
88
89
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare",
|
|
89
|
-
"lint": "eslint ."
|
|
90
|
+
"lint": "eslint .",
|
|
91
|
+
"test": "vitest run",
|
|
92
|
+
"test:watch": "vitest"
|
|
90
93
|
}
|
|
91
94
|
}
|