@progress/sitefinity-nextjs-sdk
Version:
Provides OOB widgets developed using the Next.js framework, which includes an abstraction layer for Sitefinity communication. Additionally, it offers an expanded API, typings, and tools for further development and integration.
227 lines (226 loc) • 11.1 kB
JavaScript
'use client';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import React from 'react';
import { StylingConfig } from '../styling/styling-config';
import { VisibilityStyle } from '../styling/visibility-style';
import { FormRulesExecutor } from './rules/form-rules-executor';
import { classNames } from '../../editor/utils/classNames';
import { FormContext } from './form-context';
const generateHiddenFields = (fields) => {
return fields.reduce((obj, item) => Object.assign(obj, { [item]: true }), {});
};
export function FormClient(props) {
const { children, viewProps, className, formDataAttributes } = props;
const fromElementRef = React.useRef(null);
const formRef = React.useRef(null);
const formRules = React.useRef(null);
const [disabledSubmitButton, setDisabledSubmitButton] = React.useState(false);
const [currentFormPage, setCurrentFormPage] = React.useState(0);
const [showLoading, setShowLoading] = React.useState(false);
const [showFields, setShowFields] = React.useState(true);
const [errorMessage, setErrorMessage] = React.useState();
const [successMessage, setSuccessMessage] = React.useState();
const splitHiddenFields = viewProps.hiddenFields?.split(',') || [];
const [hiddenInputs, setHiddenInputs] = React.useState(generateHiddenFields(splitHiddenFields));
const [skippedInputs, setSkippedInputs] = React.useState({});
const fieldValidatorsRef = React.useRef({});
const registerFieldValidator = React.useCallback((key, validator) => {
fieldValidatorsRef.current[key] = validator;
}, []);
const validateFields = React.useCallback((keys) => {
let allValid = true;
keys.forEach(key => {
if (hiddenInputs[key] || skippedInputs[key]) {
return;
}
const validator = fieldValidatorsRef.current[key];
if (validator) {
const isValid = validator();
if (!isValid) {
allValid = false;
}
}
});
return allValid;
}, [hiddenInputs, skippedInputs]);
const sfFormValueChanged = () => {
formRules.current.process();
};
const updateFields = React.useCallback((args) => {
if (args.show) {
setHiddenInputs(hI => {
const newHiddenFields = { ...hI };
delete newHiddenFields[args.show];
return newHiddenFields;
});
}
if (args.hide) {
setHiddenInputs(hI => {
const newHiddenFields = { ...hI };
newHiddenFields[args.hide] = true;
return newHiddenFields;
});
}
if (args.skip) {
setSkippedInputs(sI => {
const newSkippedFields = { ...sI };
delete newSkippedFields[args.skip];
return newSkippedFields;
});
}
if (args.unSkip) {
setSkippedInputs(sI => {
const newSkippedFields = { ...sI };
newSkippedFields[args.unSkip] = true;
return newSkippedFields;
});
}
}, []);
const formCreateRef = React.useCallback((node) => {
if (node !== null) {
formRef.current = node;
const fr = new FormRulesExecutor(node, updateFields);
formRules.current = fr;
fr.process();
}
}, [updateFields]);
function showSuccessMessage(message) {
setShowLoading(false);
setShowFields(false);
setSuccessMessage(message);
}
function showErrorMessage(message) {
setErrorMessage(message);
setShowLoading(false);
setShowFields(false);
}
function triggerLoading() {
setErrorMessage('');
setShowLoading(true);
setSuccessMessage('');
setShowFields(false);
}
function showSubmittedForm() {
setDisabledSubmitButton(true);
setShowLoading(false);
setSuccessMessage('');
setShowFields(true);
}
const handleResponse = (redirectUrl, successMessageVal, openInNewWindow) => {
if (redirectUrl) {
if (openInNewWindow) {
showSubmittedForm();
window.open(redirectUrl, '_blank');
}
else {
document.location.replace(redirectUrl);
}
}
else {
showSuccessMessage(successMessageVal || viewProps.successMessage || '');
}
};
const validFormSubmit = () => {
const form = fromElementRef.current;
if (viewProps.skipDataSubmission) {
handleResponse(viewProps.redirectUrl);
return false;
}
const genericFormError = 'Form could not be submitted';
const headerName = 'X-SF-ANTIFORGERY-REQUEST';
const headers = {};
headers[headerName] = '';
fetch('/sitefinity/anticsrf', { headers: headers }).then(function (csrfResponse) {
function sendSubmitRequest(headerValue) {
if (headerValue) {
headers[headerName] = headerValue;
}
const formData = new FormData(form);
formData.set('sf_antiforgery', headerValue || '');
const hiddenInputsTrueKeys = Object.entries(hiddenInputs).filter(([key, value]) => value === true).map(([key, value]) => key);
formData.set('sf_FormHiddenFields', hiddenInputsTrueKeys.join(','));
fetch(viewProps.submitUrl, { method: 'POST', body: formData }).then(function (formSubmitResponse) {
formSubmitResponse.json().then(function (jsonFormSubmitResponse) {
// Successfull request statuses
if (formSubmitResponse.status >= 200 && formSubmitResponse.status < 300) {
if (jsonFormSubmitResponse.success) {
if (viewProps.customSubmitAction.toString().toLowerCase() === 'true') {
handleResponse(viewProps.redirectUrl);
}
else {
handleResponse(jsonFormSubmitResponse.redirectUrl, jsonFormSubmitResponse.message, jsonFormSubmitResponse.openInNewWindow);
}
}
else {
showErrorMessage(jsonFormSubmitResponse.error);
}
// Client and Server error request statuses
}
else if (formSubmitResponse.status >= 400 && formSubmitResponse.status < 600) {
showErrorMessage(jsonFormSubmitResponse.error);
}
}, function (error) {
if (formSubmitResponse.status === 413) {
showErrorMessage('Request was too large');
}
else {
showErrorMessage(genericFormError);
}
});
}, function (error) {
const serializedError = JSON.stringify(error);
if (serializedError === '{}') {
showErrorMessage(genericFormError);
}
else {
showErrorMessage(serializedError);
}
});
}
if (csrfResponse.status === 200) {
if (csrfResponse.headers.get('content-type') === 'application/json') {
csrfResponse.json().then(function (jsonCsrfResponse) {
sendSubmitRequest(jsonCsrfResponse.Value);
});
}
else {
sendSubmitRequest();
}
}
else if (csrfResponse.status === 204 || csrfResponse.status === 404) {
sendSubmitRequest();
}
else {
showErrorMessage('Failed to submit form due to lack of csrf token');
}
});
};
const handleSubmit = function (e) {
e?.preventDefault();
const allKeys = Object.keys(fieldValidatorsRef.current);
const allValid = validateFields(allKeys);
if (allValid) {
validFormSubmit();
}
};
return (_jsxs("form", { ref: fromElementRef, action: viewProps.submitUrl, onSubmit: handleSubmit, method: "post", ...formDataAttributes, noValidate: true, children: [_jsx("div", { "data-sf-role": "success-message", className: classNames('valid-feedback', successMessage
? [StylingConfig.VisibilityClasses[VisibilityStyle.Visible]]
: [StylingConfig.VisibilityClasses[VisibilityStyle.Hidden]]), role: "alert", "aria-live": "assertive", children: successMessage || viewProps.successMessage }), _jsx("div", { "data-sf-role": "error-message", className: classNames('invalid-feedback', errorMessage
? [StylingConfig.VisibilityClasses[VisibilityStyle.Visible]]
: [StylingConfig.VisibilityClasses[VisibilityStyle.Hidden]]), role: "alert", "aria-live": "assertive", children: errorMessage }), _jsx("div", { "data-sf-role": "loading", className: classNames(showLoading
? [StylingConfig.VisibilityClasses[VisibilityStyle.Visible]]
: [StylingConfig.VisibilityClasses[VisibilityStyle.Hidden]]), children: _jsx("div", { className: "d-flex justify-content-center", children: _jsx("div", { className: "spinner-border", role: "status", children: _jsx("span", { className: "visually-hidden", children: "Loading..." }) }) }) }), _jsx(FormContext.Provider, { value: {
formViewProps: viewProps,
sfFormValueChanged,
hiddenInputs,
skippedInputs,
disabledSubmitButton,
totalFormPages: viewProps.formModel?.ComponentContext.Components.filter((c) => c.Name === 'SitefinityFormPage').length || 0,
currentFormPage,
setCurrentFormPage,
registerFieldValidator,
validateFields
}, children: _jsx("div", { ref: formCreateRef, className: classNames(className, showFields
? [StylingConfig.VisibilityClasses[VisibilityStyle.Visible]]
: [StylingConfig.VisibilityClasses[VisibilityStyle.Hidden]]), "data-sf-role": "form-container", "data-sf-invalid": viewProps.invalidClass, "data-sf-visibility-inline-visible": viewProps.visibilityClasses[VisibilityStyle.InlineVisible], "data-sf-visibility-hidden": viewProps.visibilityClasses[VisibilityStyle.Hidden], "data-sf-visibility-visible": viewProps.visibilityClasses[VisibilityStyle.Visible], children: children }) })] }));
}