@modular-forms/qwik
Version:
The modular and type-safe form library for Qwik
1,265 lines (1,264 loc) • 45.8 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value2) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value: value2 }) : obj[key] = value2;
var __publicField = (obj, key, value2) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value2);
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const qwik = require("@builder.io/qwik");
const qwikCity = require("@builder.io/qwik-city");
const requestHandler = require("@builder.io/qwik-city/middleware/request-handler");
const build = require("@builder.io/qwik/build");
const decodeFormdata = require("decode-formdata");
const valibot = require("valibot");
const jsxRuntime = require("@builder.io/qwik/jsx-runtime");
class FormError extends Error {
constructor(arg1, arg2) {
super(typeof arg1 === "string" ? arg1 : "");
__publicField(this, "name", "FormError");
__publicField(this, "errors");
this.errors = typeof arg1 === "string" ? arg2 || {} : arg1;
}
}
function formActionQrl(action, arg2) {
return qwikCity.globalActionQrl(qwik.$(async (jsonData, event) => {
const { validate: validate2, ...formDataInfo } = typeof arg2 === "object" ? arg2 : {
validate: arg2
};
const type = event.request.headers.get("content-type")?.split(/[;,]/, 1)[0];
const values = type === "application/x-www-form-urlencoded" || type === "multipart/form-data" ? decodeFormdata.decode(event.sharedMap.get("@actionFormData"), formDataInfo, ({ output }) => output instanceof Blob ? qwik.noSerialize(output) : output) : jsonData;
const errors = validate2 ? await validate2(values) : {};
let formActionStore = {
values,
errors,
response: {}
};
if (!Object.keys(errors).length) {
try {
const result = await action(values, event);
if (result && typeof result === "object") {
formActionStore = {
values,
errors: result.errors || {},
response: {
status: result.status,
message: result.message,
data: result.data
}
};
}
} catch (error) {
if (error instanceof requestHandler.AbortMessage || build.isDev && (error?.constructor?.name === "AbortMessage" || error?.constructor?.name === "RedirectMessage")) {
throw error;
} else {
console.error(error);
if (error instanceof FormError) {
formActionStore = {
values,
errors: error.errors,
response: {
status: "error",
message: error.message
}
};
} else {
formActionStore.response = {
status: "error",
message: "An unknown error has occurred."
};
}
}
}
}
return formActionStore;
}), {
id: action.getHash()
});
}
const formAction$ = qwik.implicit$FirstArg(formActionQrl);
function valiFieldQrl(schema) {
return qwik.$(async (value2) => {
const resolvedSchema = await schema.resolve();
const result = await valibot.safeParseAsync(typeof resolvedSchema === "function" ? resolvedSchema() : resolvedSchema, value2, {
abortPipeEarly: true
});
return result.issues?.[0].message || "";
});
}
const valiField$ = qwik.implicit$FirstArg(valiFieldQrl);
function valiFormQrl(schema) {
return qwik.$(async (values) => {
const resolvedSchema = await schema.resolve();
const result = await valibot.safeParseAsync(typeof resolvedSchema === "function" ? resolvedSchema() : resolvedSchema, values, {
abortPipeEarly: true
});
const formErrors = {};
if (result.issues) {
for (const issue of result.issues) {
formErrors[valibot.getDotPath(issue)] = issue.message;
}
}
return formErrors;
});
}
const valiForm$ = qwik.implicit$FirstArg(valiFormQrl);
function getElementInput(element, field, type) {
const { checked, files, options, value: value2, valueAsDate, valueAsNumber } = element;
return !type || type === "string" ? value2 : type === "string[]" ? options ? [
...options
].filter((e) => e.selected && !e.disabled).map((e) => e.value) : checked ? [
...field.value || [],
value2
] : (field.value || []).filter((v) => v !== value2) : type === "number" ? valueAsNumber : type === "boolean" ? checked : type === "File" && files ? qwik.noSerialize(files[0]) : type === "File[]" && files ? [
...files
].map((file) => qwik.noSerialize(file)) : type === "Date" && valueAsDate ? valueAsDate : field.value;
}
function getFieldAndArrayStores(form) {
return [
...Object.values(form.internal.fields),
...Object.values(form.internal.fieldArrays)
];
}
function getFieldArrayStore(form, name) {
return form.internal.fieldArrays[name];
}
function getPathIndex(name, path) {
return +path.replace(`${name}.`, "").split(".")[0];
}
function removeInvalidNames(form, names) {
getFieldArrayNames(form, false).forEach((fieldArrayName) => {
const lastIndex = getFieldArrayStore(form, fieldArrayName).items.length - 1;
names.filter((name) => name.startsWith(`${fieldArrayName}.`) && getPathIndex(fieldArrayName, name) > lastIndex).forEach((name) => {
names.splice(names.indexOf(name), 1);
});
});
}
function getFieldArrayNames(form, shouldValid = true) {
const fieldArrayNames = Object.keys(form.internal.fieldArrays);
if (shouldValid) {
removeInvalidNames(form, fieldArrayNames);
}
return fieldArrayNames;
}
function getFieldArrayState(form, name) {
const fieldArray = getFieldArrayStore(form, name);
return fieldArray ? {
startItems: fieldArray.internal.startItems,
items: fieldArray.items,
error: fieldArray.error,
touched: fieldArray.touched,
dirty: fieldArray.dirty
} : void 0;
}
function getFieldNames(form, shouldValid = true) {
const fieldNames = Object.keys(form.internal.fields);
if (shouldValid) {
removeInvalidNames(form, fieldNames);
}
return fieldNames;
}
function getFieldStore(form, name) {
return form.internal.fields[name];
}
function getFieldState(form, name) {
const field = getFieldStore(form, name);
return field ? {
startValue: field.internal.startValue,
value: field.value,
error: field.error,
touched: field.touched,
dirty: field.dirty
} : void 0;
}
function getFilteredNames(form, arg2, shouldValid) {
const allFieldNames = getFieldNames(form, shouldValid);
const allFieldArrayNames = getFieldArrayNames(form, shouldValid);
if (typeof arg2 === "string" || Array.isArray(arg2)) {
return (typeof arg2 === "string" ? [
arg2
] : arg2).reduce((tuple, name) => {
const [fieldNames, fieldArrayNames] = tuple;
if (allFieldArrayNames.includes(name)) {
allFieldArrayNames.forEach((fieldArrayName) => {
if (fieldArrayName.startsWith(name)) {
fieldArrayNames.add(fieldArrayName);
}
});
allFieldNames.forEach((fieldName) => {
if (fieldName.startsWith(name)) {
fieldNames.add(fieldName);
}
});
} else {
fieldNames.add(name);
}
return tuple;
}, [
/* @__PURE__ */ new Set(),
/* @__PURE__ */ new Set()
]).map((set) => [
...set
]);
}
return [
allFieldNames,
allFieldArrayNames
];
}
function getInitialFieldArrayStore(name, { items, initialItems, error } = {
items: [],
initialItems: [],
error: ""
}) {
const dirty = initialItems.join() !== items.join();
return {
internal: {
initialItems: [
...initialItems
],
startItems: [
...initialItems
],
validate: [],
validateOn: void 0,
revalidateOn: void 0,
consumers: []
},
name,
items,
error,
active: false,
touched: dirty,
dirty
};
}
function isFieldDirty(startValue, currentValue) {
const toValue = (item) => item instanceof Blob ? item.size : item;
return Array.isArray(startValue) && Array.isArray(currentValue) ? startValue.map(toValue).join() !== currentValue.map(toValue).join() : startValue instanceof Date && currentValue instanceof Date ? startValue.getTime() !== currentValue.getTime() : Number.isNaN(startValue) && Number.isNaN(currentValue) ? false : startValue !== currentValue;
}
function getInitialFieldStore(name, { value: value2, initialValue, error } = {
value: void 0,
initialValue: void 0,
error: ""
}) {
const dirty = isFieldDirty(initialValue, value2);
return {
internal: {
initialValue,
startValue: initialValue,
validate: [],
validateOn: void 0,
revalidateOn: void 0,
transform: [],
elements: [],
consumers: []
},
name,
value: value2,
error,
active: false,
touched: dirty,
dirty
};
}
function getPathValue(path, object) {
return path.split(".").reduce((value2, key) => value2?.[key], object);
}
let counter = 0;
function getUniqueId() {
return counter++;
}
function getInitialStores({ loader, action, fieldArrays }) {
function getActionValue(name) {
return action?.value?.values && getPathValue(name, action.value.values);
}
const generateItems = () => getUniqueId();
const getActionError = (name) => action?.value?.errors[name] || "";
const createInitialStores = (stores, data, prevPath) => Object.entries(data).reduce((stores2, [path, value2]) => {
const compoundPath = prevPath ? `${prevPath}.${path}` : path;
if (fieldArrays?.includes(compoundPath.replace(/.\d+./g, ".$."))) {
const initialItems = value2.map(generateItems);
stores2[1][compoundPath] = getInitialFieldArrayStore(compoundPath, {
initialItems,
items: getActionValue(compoundPath)?.map(generateItems) || [
...initialItems
],
error: getActionError(compoundPath)
});
} else if (!value2 || typeof value2 !== "object" || Array.isArray(value2) || value2 instanceof Date) {
stores2[0][compoundPath] = getInitialFieldStore(compoundPath, {
initialValue: value2,
value: getActionValue(compoundPath) ?? value2,
error: getActionError(compoundPath)
});
}
if (value2 && typeof value2 === "object") {
createInitialStores(stores2, value2, compoundPath);
}
return stores2;
}, stores);
return createInitialStores([
{},
{}
], loader.value);
}
async function getParsedZodSchema(schema, value2) {
const zodSchema = await schema.resolve();
return (typeof zodSchema === "function" ? zodSchema() : zodSchema).safeParseAsync(value2);
}
function getOptions(arg1, arg2) {
return (typeof arg1 !== "string" && !Array.isArray(arg1) ? arg1 : arg2) || {};
}
function updateFormDirty(form, dirty) {
form.dirty = dirty || getFieldAndArrayStores(form).some((fieldOrFieldArray) => fieldOrFieldArray.active && fieldOrFieldArray.dirty);
}
function updateFieldDirty(form, field) {
const dirty = isFieldDirty(field.internal.startValue, field.value);
if (dirty !== field.dirty) {
field.dirty = dirty;
updateFormDirty(form, dirty);
}
}
function focus(form, name) {
getFieldStore(form, name)?.internal.elements[0]?.focus();
}
function setError(form, name, error, { shouldActive = true, shouldTouched = false, shouldDirty = false, shouldFocus = !!error } = {}) {
for (const fieldOrFieldArray of [
getFieldStore(form, name),
getFieldArrayStore(form, name)
]) {
if (fieldOrFieldArray && (!shouldActive || fieldOrFieldArray.active) && (!shouldTouched || fieldOrFieldArray.touched) && (!shouldDirty || fieldOrFieldArray.dirty)) {
fieldOrFieldArray.error = error;
if (error && "value" in fieldOrFieldArray && shouldFocus) {
focus(form, name);
}
}
}
updateFormInvalid(form, !!error);
}
function clearError(form, name, options) {
setError(form, name, "", options);
}
function clearResponse(form) {
form.response = {};
}
function getError(form, name, { shouldActive = true, shouldTouched = false, shouldDirty = false } = {}) {
for (const fieldOrFieldArray of [
getFieldStore(form, name),
getFieldArrayStore(form, name)
]) {
if (fieldOrFieldArray && (!shouldActive || fieldOrFieldArray.active) && (!shouldTouched || fieldOrFieldArray.touched) && (!shouldDirty || fieldOrFieldArray.dirty)) {
return fieldOrFieldArray.error;
}
}
return void 0;
}
function getErrors(form, arg2, arg3) {
const [fieldNames, fieldArrayNames] = getFilteredNames(form, arg2);
const { shouldActive = true, shouldTouched = false, shouldDirty = false } = getOptions(arg2, arg3);
return [
...fieldNames.map((name) => [
name,
getFieldStore(form, name)
]),
...fieldArrayNames.map((name) => [
name,
getFieldArrayStore(form, name)
])
].reduce((formErrors, [name, fieldOrFieldArray]) => {
if (fieldOrFieldArray.error && (!shouldActive || fieldOrFieldArray.active) && (!shouldTouched || fieldOrFieldArray.touched) && (!shouldDirty || fieldOrFieldArray.dirty)) {
formErrors[name] = fieldOrFieldArray.error;
}
return formErrors;
}, {});
}
function getValue(form, name, { shouldActive = true, shouldTouched = false, shouldDirty = false, shouldValid = false } = {}) {
const field = getFieldStore(form, name);
if (field && (!shouldActive || field.active) && (!shouldTouched || field.touched) && (!shouldDirty || field.dirty) && (!shouldValid || !field.error)) {
return field.value;
}
return void 0;
}
function getValues(form, arg2, arg3) {
const { shouldActive = true, shouldTouched = false, shouldDirty = false, shouldValid = false } = getOptions(arg2, arg3);
return getFilteredNames(form, arg2)[0].reduce((values, name) => {
const field = getFieldStore(form, name);
if ((!shouldActive || field.active) && (!shouldTouched || field.touched) && (!shouldDirty || field.dirty) && (!shouldValid || !field.error)) {
(typeof arg2 === "string" ? name.replace(`${arg2}.`, "") : name).split(".").reduce((object, key, index, keys) => object[key] = index === keys.length - 1 ? field.value : typeof object[key] === "object" && object[key] || (isNaN(+keys[index + 1]) ? {} : []), values);
}
return values;
}, typeof arg2 === "string" ? [] : {});
}
function hasField(form, name, { shouldActive = true, shouldTouched = false, shouldDirty = false, shouldValid = false } = {}) {
const field = getFieldStore(form, name);
return !!field && (!shouldActive || field.active) && (!shouldTouched || field.touched) && (!shouldDirty || field.dirty) && (!shouldValid || !field.error);
}
function hasFieldArray(form, name, { shouldActive = true, shouldTouched = false, shouldDirty = false, shouldValid = false } = {}) {
const fieldArray = getFieldArrayStore(form, name);
return !!fieldArray && (!shouldActive || fieldArray.active) && (!shouldTouched || fieldArray.touched) && (!shouldDirty || fieldArray.dirty) && (!shouldValid || !fieldArray.error);
}
function insert(form, name, options) {
const fieldArray = getFieldArrayStore(form, name);
if (fieldArray) {
const arrayLength = fieldArray.items.length;
const { at: index = arrayLength, value: value2 } = options;
if (index >= 0 && index <= arrayLength) {
if (index < arrayLength) {
const filterName = (value22) => value22.startsWith(`${name}.`) && getPathIndex(name, value22) >= index;
const getNextIndexName = (fieldOrFieldArrayName, fieldOrFieldArrayIndex) => fieldOrFieldArrayName.replace(`${name}.${fieldOrFieldArrayIndex}`, `${name}.${fieldOrFieldArrayIndex + 1}`);
getFieldNames(form).filter(filterName).sort(sortArrayPathIndex(name)).reverse().forEach((fieldName) => {
setFieldState(form, getNextIndexName(fieldName, getPathIndex(name, fieldName)), getFieldState(form, fieldName));
});
getFieldArrayNames(form).filter(filterName).sort(sortArrayPathIndex(name)).reverse().forEach((fieldArrayName) => {
setFieldArrayState(form, getNextIndexName(fieldArrayName, getPathIndex(name, fieldArrayName)), getFieldArrayState(form, fieldArrayName));
});
}
setFieldArrayValue(form, name, {
at: index,
value: value2
});
fieldArray.items.splice(index, 0, getUniqueId());
fieldArray.touched = true;
form.touched = true;
fieldArray.dirty = true;
form.dirty = true;
setTimeout(() => validateIfRequired(form, fieldArray, name, {
on: [
"touched",
"input"
]
}), 250);
}
}
}
function move(form, name, { from: fromIndex, to: toIndex }) {
const fieldArray = getFieldArrayStore(form, name);
if (fieldArray) {
const lastIndex = fieldArray.items.length - 1;
if (fromIndex >= 0 && fromIndex <= lastIndex && toIndex >= 0 && toIndex <= lastIndex && fromIndex !== toIndex) {
const filterName = (value2) => {
if (value2.startsWith(name)) {
const fieldIndex = getPathIndex(name, value2);
return fieldIndex >= fromIndex && fieldIndex <= toIndex || fieldIndex <= fromIndex && fieldIndex >= toIndex;
}
};
const getPrevIndexName = (fieldOrFieldArrayName, fieldOrFieldArrayIndex) => fieldOrFieldArrayName.replace(`${name}.${fieldOrFieldArrayIndex}`, fromIndex < toIndex ? `${name}.${fieldOrFieldArrayIndex - 1}` : `${name}.${fieldOrFieldArrayIndex + 1}`);
const getToIndexName = (fieldOrFieldArrayName) => fieldOrFieldArrayName.replace(`${name}.${fromIndex}`, `${name}.${toIndex}`);
const fieldNames = getFieldNames(form).filter(filterName).sort(sortArrayPathIndex(name));
const fieldArrayNames = getFieldArrayNames(form).filter(filterName).sort(sortArrayPathIndex(name));
if (fromIndex > toIndex) {
fieldNames.reverse();
fieldArrayNames.reverse();
}
const fieldStateMap = /* @__PURE__ */ new Map();
const fieldArrayStateMap = /* @__PURE__ */ new Map();
fieldNames.forEach((fieldName) => {
const fieldState = getFieldState(form, fieldName);
const fieldIndex = getPathIndex(name, fieldName);
if (fieldIndex === fromIndex) {
fieldStateMap.set(fieldName, fieldState);
} else {
setFieldState(form, getPrevIndexName(fieldName, fieldIndex), fieldState);
}
});
fieldStateMap.forEach((fieldState, fieldName) => {
setFieldState(form, getToIndexName(fieldName), fieldState);
});
fieldArrayNames.forEach((fieldArrayName) => {
const fieldArrayState = getFieldArrayState(form, fieldArrayName);
const fieldArrayIndex = getPathIndex(name, fieldArrayName);
if (fieldArrayIndex === fromIndex) {
fieldArrayStateMap.set(fieldArrayName, fieldArrayState);
} else {
setFieldArrayState(form, getPrevIndexName(fieldArrayName, fieldArrayIndex), fieldArrayState);
}
});
fieldArrayStateMap.forEach((fieldArrayState, fieldArrayName) => {
setFieldArrayState(form, getToIndexName(fieldArrayName), fieldArrayState);
});
fieldArray.items.splice(toIndex, 0, fieldArray.items.splice(fromIndex, 1)[0]);
fieldArray.touched = true;
form.touched = true;
updateFieldArrayDirty(form, fieldArray);
}
}
}
function remove(form, name, { at: index }) {
const fieldArray = getFieldArrayStore(form, name);
if (fieldArray) {
const lastIndex = fieldArray.items.length - 1;
if (index >= 0 && index <= lastIndex) {
const filterName = (value2) => value2.startsWith(`${name}.`) && getPathIndex(name, value2) > index;
const getPrevIndexName = (fieldOrFieldArrayName, fieldOrFieldArrayIndex) => fieldOrFieldArrayName.replace(`${name}.${fieldOrFieldArrayIndex}`, `${name}.${fieldOrFieldArrayIndex - 1}`);
getFieldNames(form).filter(filterName).sort(sortArrayPathIndex(name)).forEach((fieldName) => {
setFieldState(form, getPrevIndexName(fieldName, getPathIndex(name, fieldName)), getFieldState(form, fieldName));
});
getFieldArrayNames(form).filter(filterName).sort(sortArrayPathIndex(name)).forEach((fieldArrayName) => {
setFieldArrayState(form, getPrevIndexName(fieldArrayName, getPathIndex(name, fieldArrayName)), getFieldArrayState(form, fieldArrayName));
});
fieldArray.items.splice(index, 1);
fieldArray.touched = true;
form.touched = true;
updateFieldArrayDirty(form, fieldArray);
validateIfRequired(form, fieldArray, name, {
on: [
"touched",
"input"
]
});
}
}
}
function replace(form, name, options) {
const fieldArray = getFieldArrayStore(form, name);
if (fieldArray) {
const { at: index } = options;
const lastIndex = fieldArray.items.length - 1;
if (index >= 0 && index <= lastIndex) {
setFieldArrayValue(form, name, options);
fieldArray.items[index] = getUniqueId();
fieldArray.touched = true;
form.touched = true;
fieldArray.dirty = true;
form.dirty = true;
}
}
}
function reset(form, arg2, arg3) {
const [fieldNames, fieldArrayNames] = getFilteredNames(form, arg2, false);
const resetSingleField = typeof arg2 === "string" && fieldNames.length === 1;
const resetEntireForm = !resetSingleField && !Array.isArray(arg2);
const options = getOptions(arg2, arg3);
const { initialValue, initialValues, keepResponse = false, keepSubmitCount = false, keepSubmitted = false, keepValues = false, keepDirtyValues = false, keepItems = false, keepDirtyItems = false, keepErrors = false, keepTouched = false, keepDirty = false } = options;
fieldNames.forEach((name) => {
const field = getFieldStore(form, name);
if (resetSingleField ? "initialValue" in options : initialValues) {
field.internal.initialValue = resetSingleField ? initialValue : getPathValue(name, initialValues);
}
const keepDirtyValue = keepDirtyValues && field.dirty;
if (!keepValues && !keepDirtyValue) {
field.internal.startValue = field.internal.initialValue;
field.value = field.internal.initialValue;
field.internal.elements.forEach((element) => {
if (element.type === "file") {
element.value = "";
}
});
}
if (!keepTouched) {
field.touched = false;
}
if (!keepDirty && !keepValues && !keepDirtyValue) {
field.dirty = false;
}
if (!keepErrors) {
field.error = "";
}
});
fieldArrayNames.forEach((name) => {
const fieldArray = getFieldArrayStore(form, name);
const keepCurrentDirtyItems = keepDirtyItems && fieldArray.dirty;
if (!keepItems && !keepCurrentDirtyItems) {
if (initialValues) {
fieldArray.internal.initialItems = getPathValue(name, initialValues)?.map(() => getUniqueId()) || [];
}
fieldArray.internal.startItems = [
...fieldArray.internal.initialItems
];
fieldArray.items = [
...fieldArray.internal.initialItems
];
}
if (!keepTouched) {
fieldArray.touched = false;
}
if (!keepDirty && !keepItems && !keepCurrentDirtyItems) {
fieldArray.dirty = false;
}
if (!keepErrors) {
fieldArray.error = "";
}
});
if (resetEntireForm) {
if (!keepResponse) {
form.response = {};
}
if (!keepSubmitCount) {
form.submitCount = 0;
}
if (!keepSubmitted) {
form.submitted = false;
}
}
updateFormState(form);
}
function setResponse(form, response, { duration } = {}) {
form.response = response;
if (duration) {
setTimeout(() => {
if (form.response === response) {
form.response = {};
}
}, duration);
}
}
function setValue(form, name, value2, { shouldTouched = true, shouldDirty = true, shouldValidate = true, shouldFocus = true } = {}) {
const field = initializeFieldStore(form, name);
field.value = value2;
if (shouldTouched) {
field.touched = true;
form.touched = true;
}
if (shouldDirty) {
updateFieldDirty(form, field);
}
if (shouldValidate) {
validateIfRequired(form, field, name, {
on: [
"touched",
"input"
],
shouldFocus
});
}
}
async function validate(form, arg2, arg3) {
const [fieldNames, fieldArrayNames] = getFilteredNames(form, arg2);
const { shouldActive = true, shouldFocus = true } = getOptions(arg2, arg3);
const validator = getUniqueId();
form.internal.validators.push(validator);
form.validating = true;
const formErrors = form.internal.validate ? await form.internal.validate(getValues(form, {
shouldActive
})) : {};
let valid = typeof arg2 !== "string" && !Array.isArray(arg2) ? !Object.keys(formErrors).length : true;
const [errorFields] = await Promise.all([
// Validate each field in list
Promise.all(fieldNames.map(async (name) => {
const field = getFieldStore(form, name);
if (!shouldActive || field.active) {
let localError;
for (const validation of field.internal.validate) {
localError = await validation(field.value);
if (localError) {
break;
}
}
const fieldError = localError || formErrors[name] || "";
if (fieldError) {
valid = false;
}
field.error = fieldError;
return fieldError ? name : null;
}
})),
// Validate each field array in list
Promise.all(fieldArrayNames.map(async (name) => {
const fieldArray = getFieldArrayStore(form, name);
if (!shouldActive || fieldArray.active) {
let localError = "";
for (const validation of fieldArray.internal.validate) {
localError = await validation(fieldArray.items);
if (localError) {
break;
}
}
const fieldArrayError = localError || formErrors[name] || "";
if (fieldArrayError) {
valid = false;
}
fieldArray.error = fieldArrayError;
}
}))
]);
setErrorResponse(form, formErrors, {
shouldActive
});
if (shouldFocus) {
const name = errorFields.find((name2) => name2);
if (name) {
focus(form, name);
}
}
updateFormInvalid(form, !valid);
form.internal.validators.splice(form.internal.validators.indexOf(validator), 1);
if (!form.internal.validators.length) {
form.validating = false;
}
return valid;
}
function setValues(form, arg2, arg3, arg4) {
const isFieldArray = typeof arg2 === "string";
const values = isFieldArray ? arg3 : arg2;
const options = (isFieldArray ? arg4 : arg3) || {};
const { shouldTouched = true, shouldDirty = true, shouldValidate = true, shouldFocus = true } = options;
const names = isFieldArray ? [
arg2
] : [];
const setFieldArrayItems = (name, value2) => {
const fieldArray = initializeFieldArrayStore(form, name);
fieldArray.items = value2.map(() => getUniqueId());
if (shouldTouched) {
fieldArray.touched = true;
form.touched = true;
}
if (shouldDirty) {
fieldArray.dirty = true;
form.dirty = true;
}
};
const setNestedValues = (data, prevPath) => Object.entries(data).forEach(([path, value2]) => {
const compoundPath = prevPath ? `${prevPath}.${path}` : path;
if (!value2 || typeof value2 !== "object" || !Array.isArray(value2)) {
setValue(form, compoundPath, value2, {
...options,
shouldValidate: false
});
names.push(compoundPath);
}
if (Array.isArray(value2)) {
setFieldArrayItems(compoundPath, value2);
}
if (value2 && typeof value2 === "object") {
setNestedValues(value2, compoundPath);
}
});
if (isFieldArray) {
setFieldArrayItems(arg2, arg3);
}
setNestedValues(values, isFieldArray ? arg2 : void 0);
if (shouldValidate && [
"touched",
"input"
].includes(form.internal.validateOn === "submit" && form.submitted ? form.internal.revalidateOn : form.internal.validateOn)) {
validate(form, names, {
shouldFocus
});
}
}
function submit(form) {
form.element?.requestSubmit();
}
function swap(form, name, { at: index1, and: index2 }) {
const fieldArray = getFieldArrayStore(form, name);
if (fieldArray) {
const lastIndex = fieldArray.items.length - 1;
if (index1 >= 0 && index1 <= lastIndex && index2 >= 0 && index2 <= lastIndex && index1 !== index2) {
const index1Prefix = `${name}.${index1}`;
const index2Prefix = `${name}.${index2}`;
const fieldStateMap = /* @__PURE__ */ new Map();
const fieldArrayStateMap = /* @__PURE__ */ new Map();
const filterName = (value2) => value2.startsWith(`${name}.`) && [
index1,
index2
].includes(getPathIndex(name, value2));
const swapIndex = (value2) => value2.startsWith(index1Prefix) ? value2.replace(index1Prefix, index2Prefix) : value2.replace(index2Prefix, index1Prefix);
getFieldNames(form).filter(filterName).forEach((fieldName) => fieldStateMap.set(fieldName, getFieldState(form, fieldName)));
fieldStateMap.forEach((fieldState, fieldName) => setFieldState(form, swapIndex(fieldName), fieldState));
getFieldArrayNames(form).filter(filterName).forEach((fieldArrayName) => fieldArrayStateMap.set(fieldArrayName, getFieldArrayState(form, fieldArrayName)));
fieldArrayStateMap.forEach((fieldArrayState, fieldArrayName) => setFieldArrayState(form, swapIndex(fieldArrayName), fieldArrayState));
const itemIndex1 = fieldArray.items[index1];
fieldArray.items[index1] = fieldArray.items[index2];
fieldArray.items[index2] = itemIndex1;
fieldArray.touched = true;
form.touched = true;
updateFieldArrayDirty(form, fieldArray);
}
}
}
function validateIfRequired(form, fieldOrFieldArray, name, { on: modes, shouldFocus = false }) {
const validateOn = fieldOrFieldArray.internal.validateOn ?? form.internal.validateOn;
const revalidateOn = fieldOrFieldArray.internal.revalidateOn ?? form.internal.revalidateOn;
if (modes.includes((validateOn === "submit" ? form.submitted : fieldOrFieldArray.error) ? revalidateOn : validateOn)) {
validate(form, name, {
shouldFocus
});
}
}
async function handleFieldEvent(form, field, name, event, element, validationModes, inputValue) {
if (inputValue !== void 0) {
field.value = inputValue;
}
for (const transformation of field.internal.transform) {
field.value = await transformation(field.value, event, element);
}
field.touched = true;
form.touched = true;
updateFieldDirty(form, field);
validateIfRequired(form, field, name, {
on: validationModes
});
}
function initializeFieldArrayStore(form, name) {
if (!getFieldArrayStore(form, name)) {
form.internal.fieldArrays[name] = getInitialFieldArrayStore(name);
}
return getFieldArrayStore(form, name);
}
function initializeFieldStore(form, name) {
if (!getFieldStore(form, name)) {
form.internal.fields[name] = getInitialFieldStore(name);
}
return getFieldStore(form, name);
}
function setErrorResponse(form, formErrors, { duration, shouldActive = true }) {
const message = Object.entries(formErrors).reduce((errors, [name, error]) => {
if ([
getFieldStore(form, name),
getFieldArrayStore(form, name)
].every((fieldOrFieldArray) => !fieldOrFieldArray || shouldActive && !fieldOrFieldArray.active)) {
errors.push(error);
}
return errors;
}, []).join(" ");
if (message) {
setResponse(form, {
status: "error",
message
}, {
duration
});
}
}
function setFieldArrayState(form, name, state) {
const fieldArray = initializeFieldArrayStore(form, name);
fieldArray.internal.startItems = state.startItems;
fieldArray.items = state.items;
fieldArray.error = state.error;
fieldArray.touched = state.touched;
fieldArray.dirty = state.dirty;
}
function setFieldState(form, name, state) {
const field = initializeFieldStore(form, name);
field.internal.startValue = state.startValue;
field.value = state.value;
field.error = state.error;
field.touched = state.touched;
field.dirty = state.dirty;
}
function setFieldArrayValue(form, name, { at: index, value: value2 }) {
const updateStores = (prevPath, data) => {
Object.entries(data).forEach(([path, value22]) => {
const compoundPath = `${prevPath}.${path}`;
if (form.internal.fieldArrayPaths?.includes(compoundPath.replace(/.\d+./g, ".$."))) {
const items = value22.map(() => getUniqueId());
setFieldArrayState(form, compoundPath, {
startItems: [
...items
],
items,
error: "",
touched: false,
dirty: false
});
} else if (!value22 || typeof value22 !== "object" || Array.isArray(value22) || value22 instanceof Date || value22 instanceof Blob) {
setFieldState(form, compoundPath, {
startValue: value22,
value: value22,
error: "",
touched: false,
dirty: false
});
}
if (value22 && typeof value22 === "object") {
updateStores(compoundPath, value22);
}
});
};
updateStores(name, {
[index]: value2
});
}
function setFieldErrors(form, errors, options) {
Object.entries(errors).forEach(([name, error]) => {
if (error) {
setError(form, name, error, {
...options,
shouldFocus: false
});
}
});
}
function sortArrayPathIndex(name) {
return (pathA, pathB) => getPathIndex(name, pathA) - getPathIndex(name, pathB);
}
function updateFieldArrayDirty(form, fieldArray) {
const dirty = fieldArray.internal.startItems.join() !== fieldArray.items.join();
if (dirty !== fieldArray.dirty) {
fieldArray.dirty = dirty;
updateFormDirty(form, dirty);
}
}
function updateFormInvalid(form, invalid) {
form.invalid = invalid || getFieldAndArrayStores(form).some((fieldOrFieldArray) => fieldOrFieldArray.active && fieldOrFieldArray.error);
}
function updateFormState(form) {
let touched = false, dirty = false, invalid = false;
for (const fieldOrFieldArray of getFieldAndArrayStores(form)) {
if (fieldOrFieldArray.active) {
if (fieldOrFieldArray.touched) {
touched = true;
}
if (fieldOrFieldArray.dirty) {
dirty = true;
}
if (fieldOrFieldArray.error) {
invalid = true;
}
}
if (touched && dirty && invalid) {
break;
}
}
form.touched = touched;
form.dirty = dirty;
form.invalid = invalid;
}
function zodFieldQrl(schema) {
return qwik.$(async (value2) => {
const result = await getParsedZodSchema(schema, value2);
return result.success ? "" : result.error.issues[0].message;
});
}
const zodField$ = qwik.implicit$FirstArg(zodFieldQrl);
function zodFormQrl(schema) {
return qwik.$(async (values) => {
const result = await getParsedZodSchema(schema, values);
const formErrors = {};
if (!result.success) {
for (const issue of result.error.issues) {
const path = issue.path.join(".");
if (!formErrors[path]) {
formErrors[path] = issue.message;
}
}
}
return formErrors;
});
}
const zodForm$ = qwik.implicit$FirstArg(zodFormQrl);
function Lifecycle(props, key, flags) {
return qwik.component$(({ of: form, store, validate: validate2, validateOn, revalidateOn, transform, keepActive = false, keepState = true }) => {
qwik.useVisibleTask$(({ cleanup }) => {
store.internal.validate = validate2 ? Array.isArray(validate2) ? validate2 : [
validate2
] : [];
store.internal.validateOn = validateOn;
store.internal.revalidateOn = revalidateOn;
if ("value" in store) {
store.internal.transform = transform ? Array.isArray(transform) ? transform : [
transform
] : [];
}
const consumer = getUniqueId();
store.internal.consumers.push(consumer);
if (!store.active) {
store.active = true;
updateFormState(form);
}
cleanup(() => setTimeout(() => {
store.internal.consumers.splice(store.internal.consumers.indexOf(consumer), 1);
if (!keepActive && !store.internal.consumers.length) {
store.active = false;
if (!keepState) {
reset(form, store.name);
} else {
updateFormState(form);
}
}
if ("value" in store) {
store.internal.elements = store.internal.elements.filter((element) => element.isConnected);
}
}, 15));
});
return /* @__PURE__ */ jsxRuntime.jsx(qwik.Slot, {});
})(props, key, flags);
}
function Field({ children, name, type, ...props }) {
const { of: form } = props;
const field = getFieldStore(form, name);
return /* @__PURE__ */ jsxRuntime.jsx(Lifecycle, {
store: field,
...props,
children: children(field, {
name,
autoFocus: build.isServer && !!field.error,
ref: qwik.$((element) => {
field.internal.elements.push(element);
}),
onInput$: qwik.$((event, element) => {
handleFieldEvent(form, field, name, event, element, [
"touched",
"input"
], getElementInput(element, field, type));
}),
onChange$: qwik.$((event, element) => {
handleFieldEvent(form, field, name, event, element, [
"change"
]);
}),
onBlur$: qwik.$((event, element) => {
handleFieldEvent(form, field, name, event, element, [
"touched",
"blur"
]);
})
})
}, name);
}
function FieldArray({ children, name, ...props }) {
const fieldArray = getFieldArrayStore(props.of, name);
return /* @__PURE__ */ jsxRuntime.jsx(Lifecycle, {
store: fieldArray,
...props,
children: children(fieldArray)
}, name);
}
function Form({ of: form, action, onSubmit$, responseDuration: duration, keepResponse, shouldActive, shouldTouched, shouldDirty, shouldFocus, reloadDocument, children, ...formProps }) {
const { encType } = formProps;
const options = {
duration,
shouldActive,
shouldTouched,
shouldDirty,
shouldFocus
};
return /* @__PURE__ */ jsxRuntime.jsx("form", {
noValidate: true,
...formProps,
method: "post",
action: action?.actionPath,
"preventdefault:submit": !reloadDocument,
ref: (element) => {
form.element = element;
},
onSubmit$: async (event, element) => {
if (!keepResponse) {
form.response = {};
}
form.submitCount++;
form.submitted = true;
form.submitting = true;
try {
if (await validate(form, options)) {
const values = getValues(form, options);
const [actionResult] = await Promise.all([
!reloadDocument ? action?.submit(encType ? new FormData(element) : values) : void 0,
// eslint-disable-next-line qwik/valid-lexical-scope
onSubmit$?.(values, event)
]);
if (actionResult?.value) {
const { errors, response } = actionResult.value;
setFieldErrors(form, errors, {
...options,
shouldFocus: false
});
if (Object.keys(response).length) {
setResponse(form, response, options);
} else {
setErrorResponse(form, errors, options);
}
}
}
} catch (error) {
if (error instanceof FormError) {
setFieldErrors(form, error.errors, {
...options,
shouldFocus: false
});
}
if (!(error instanceof FormError) || error.message) {
setResponse(form, {
status: "error",
message: error?.message || "An unknown error has occurred."
}, options);
}
} finally {
form.submitting = false;
}
},
children
});
}
function useFormStore({ validate: validate2, validateOn = "submit", revalidateOn = "input", ...options }) {
return qwik.useStore(() => {
const [fields, fieldArrays] = getInitialStores(options);
return {
internal: {
fields,
fieldArrays,
fieldArrayPaths: options.fieldArrays,
validate: validate2,
validators: [],
validateOn,
revalidateOn
},
// FIXME: Set state based on `action`
element: void 0,
submitCount: 0,
submitting: false,
submitted: false,
validating: false,
touched: false,
dirty: false,
invalid: false,
response: options.action?.value?.response || {}
};
});
}
function useForm(options) {
const form = useFormStore(options);
return [
form,
{
Form: (props) => Form({
of: form,
action: options.action,
...props
}),
Field: (props) => Field({
of: form,
...props
}),
FieldArray: (props) => FieldArray({
of: form,
...props
})
}
];
}
function toCustomQrl(action, { on: mode }) {
return qwik.$((value2, event, element) => event.type === mode ? action(value2, event, element) : value2);
}
const toCustom$ = qwik.implicit$FirstArg(toCustomQrl);
function toLowerCase(options) {
return toCustom$((value2) => value2 && value2.toLowerCase(), options);
}
function toTrimmed(options) {
return toCustom$((value2) => value2 && value2.trim(), options);
}
function toUpperCase(options) {
return toCustom$((value2) => value2 && value2.toUpperCase(), options);
}
function customQrl(requirement, error) {
return qwik.$(async (value2) => (Array.isArray(value2) ? value2.length : value2 || value2 === 0) && !await requirement(value2) ? error : "");
}
const custom$ = qwik.implicit$FirstArg(customQrl);
function email(error) {
return qwik.$((value2) => value2 && !/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i.test(value2) ? error : "");
}
function maxLength(requirement, error) {
return qwik.$((value2) => value2?.length && value2.length > requirement ? error : "");
}
function maxRange(requirement, error) {
return qwik.$((value2) => (value2 || value2 === 0) && value2 > requirement ? error : "");
}
function maxSize(requirement, error) {
return qwik.$((value2) => value2 && (Array.isArray(value2) ? [
...value2
].some((file) => file.size > requirement) : value2.size > requirement) ? error : "");
}
function maxTotalSize(requirement, error) {
return qwik.$((value2) => value2?.length && [
...value2
].reduce((size, file) => size + file.size, 0) > requirement ? error : "");
}
function mimeType(requirement, error) {
const mimeTypes = Array.isArray(requirement) ? requirement : [
requirement
];
return qwik.$((value2) => value2 && (Array.isArray(value2) ? [
...value2
].some((file) => !mimeTypes.includes(file.type)) : !mimeTypes.includes(value2.type)) ? error : "");
}
function minLength(requirement, error) {
return qwik.$((value2) => value2?.length && value2.length < requirement ? error : "");
}
function minRange(requirement, error) {
return qwik.$((value2) => (value2 || value2 === 0) && value2 < requirement ? error : "");
}
function minSize(requirement, error) {
return qwik.$((value2) => value2 && (Array.isArray(value2) ? [
...value2
].some((file) => file.size < requirement) : value2.size < requirement) ? error : "");
}
function minTotalSize(requirement, error) {
return qwik.$((value2) => value2?.length && [
...value2
].reduce((size, file) => size + file.size, 0) < requirement ? error : "");
}
function pattern(requirement, error) {
return qwik.$((value2) => value2 && !requirement.test(value2) ? error : "");
}
function required(error) {
return qwik.$((value2) => !value2 && value2 !== 0 || Array.isArray(value2) && !value2.length ? error : "");
}
function url(error) {
return qwik.$((value2) => {
try {
value2 && new URL(value2);
return "";
} catch (_) {
return error;
}
});
}
function value(requirement, error) {
return qwik.$((value2) => (value2 || value2 === 0) && value2 !== requirement ? error : "");
}
exports.Field = Field;
exports.FieldArray = FieldArray;
exports.Form = Form;
exports.FormError = FormError;
exports.clearError = clearError;
exports.clearResponse = clearResponse;
exports.custom$ = custom$;
exports.customQrl = customQrl;
exports.email = email;
exports.focus = focus;
exports.formAction$ = formAction$;
exports.formActionQrl = formActionQrl;
exports.getError = getError;
exports.getErrors = getErrors;
exports.getValue = getValue;
exports.getValues = getValues;
exports.hasField = hasField;
exports.hasFieldArray = hasFieldArray;
exports.insert = insert;
exports.maxLength = maxLength;
exports.maxRange = maxRange;
exports.maxSize = maxSize;
exports.maxTotalSize = maxTotalSize;
exports.mimeType = mimeType;
exports.minLength = minLength;
exports.minRange = minRange;
exports.minSize = minSize;
exports.minTotalSize = minTotalSize;
exports.move = move;
exports.pattern = pattern;
exports.remove = remove;
exports.replace = replace;
exports.required = required;
exports.reset = reset;
exports.setError = setError;
exports.setResponse = setResponse;
exports.setValue = setValue;
exports.setValues = setValues;
exports.submit = submit;
exports.swap = swap;
exports.toCustom$ = toCustom$;
exports.toCustomQrl = toCustomQrl;
exports.toLowerCase = toLowerCase;
exports.toTrimmed = toTrimmed;
exports.toUpperCase = toUpperCase;
exports.url = url;
exports.useForm = useForm;
exports.useFormStore = useFormStore;
exports.valiField$ = valiField$;
exports.valiFieldQrl = valiFieldQrl;
exports.valiForm$ = valiForm$;
exports.valiFormQrl = valiFormQrl;
exports.validate = validate;
exports.value = value;
exports.zodField$ = zodField$;
exports.zodFieldQrl = zodFieldQrl;
exports.zodForm$ = zodForm$;
exports.zodFormQrl = zodFormQrl;