form-preview-df
Version:
Resusable Form Preview Components
1,149 lines (1,131 loc) • 375 kB
JavaScript
'use strict';
var React = require('react');
var reactI18next = require('react-i18next');
require('react-dom');
var jsxRuntime$1 = {exports: {}};
var reactJsxRuntime_production$1 = {};
/**
* @license React
* react-jsx-runtime.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReactJsxRuntime_production$1;
function requireReactJsxRuntime_production$1 () {
if (hasRequiredReactJsxRuntime_production$1) return reactJsxRuntime_production$1;
hasRequiredReactJsxRuntime_production$1 = 1;
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"),
REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
function jsxProd(type, config, maybeKey) {
var key = null;
void 0 !== maybeKey && (key = "" + maybeKey);
void 0 !== config.key && (key = "" + config.key);
if ("key" in config) {
maybeKey = {};
for (var propName in config)
"key" !== propName && (maybeKey[propName] = config[propName]);
} else maybeKey = config;
config = maybeKey.ref;
return {
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
ref: void 0 !== config ? config : null,
props: maybeKey
};
}
reactJsxRuntime_production$1.Fragment = REACT_FRAGMENT_TYPE;
reactJsxRuntime_production$1.jsx = jsxProd;
reactJsxRuntime_production$1.jsxs = jsxProd;
return reactJsxRuntime_production$1;
}
var reactJsxRuntime_development$1 = {};
/**
* @license React
* react-jsx-runtime.development.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReactJsxRuntime_development$1;
function requireReactJsxRuntime_development$1 () {
if (hasRequiredReactJsxRuntime_development$1) return reactJsxRuntime_development$1;
hasRequiredReactJsxRuntime_development$1 = 1;
"production" !== process.env.NODE_ENV &&
(function () {
function getComponentNameFromType(type) {
if (null == type) return null;
if ("function" === typeof type)
return type.$$typeof === REACT_CLIENT_REFERENCE
? null
: type.displayName || type.name || null;
if ("string" === typeof type) return type;
switch (type) {
case REACT_FRAGMENT_TYPE:
return "Fragment";
case REACT_PROFILER_TYPE:
return "Profiler";
case REACT_STRICT_MODE_TYPE:
return "StrictMode";
case REACT_SUSPENSE_TYPE:
return "Suspense";
case REACT_SUSPENSE_LIST_TYPE:
return "SuspenseList";
case REACT_ACTIVITY_TYPE:
return "Activity";
}
if ("object" === typeof type)
switch (
("number" === typeof type.tag &&
console.error(
"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
),
type.$$typeof)
) {
case REACT_PORTAL_TYPE:
return "Portal";
case REACT_CONTEXT_TYPE:
return type.displayName || "Context";
case REACT_CONSUMER_TYPE:
return (type._context.displayName || "Context") + ".Consumer";
case REACT_FORWARD_REF_TYPE:
var innerType = type.render;
type = type.displayName;
type ||
((type = innerType.displayName || innerType.name || ""),
(type = "" !== type ? "ForwardRef(" + type + ")" : "ForwardRef"));
return type;
case REACT_MEMO_TYPE:
return (
(innerType = type.displayName || null),
null !== innerType
? innerType
: getComponentNameFromType(type.type) || "Memo"
);
case REACT_LAZY_TYPE:
innerType = type._payload;
type = type._init;
try {
return getComponentNameFromType(type(innerType));
} catch (x) {}
}
return null;
}
function testStringCoercion(value) {
return "" + value;
}
function checkKeyStringCoercion(value) {
try {
testStringCoercion(value);
var JSCompiler_inline_result = !1;
} catch (e) {
JSCompiler_inline_result = !0;
}
if (JSCompiler_inline_result) {
JSCompiler_inline_result = console;
var JSCompiler_temp_const = JSCompiler_inline_result.error;
var JSCompiler_inline_result$jscomp$0 =
("function" === typeof Symbol &&
Symbol.toStringTag &&
value[Symbol.toStringTag]) ||
value.constructor.name ||
"Object";
JSCompiler_temp_const.call(
JSCompiler_inline_result,
"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
JSCompiler_inline_result$jscomp$0
);
return testStringCoercion(value);
}
}
function getTaskName(type) {
if (type === REACT_FRAGMENT_TYPE) return "<>";
if (
"object" === typeof type &&
null !== type &&
type.$$typeof === REACT_LAZY_TYPE
)
return "<...>";
try {
var name = getComponentNameFromType(type);
return name ? "<" + name + ">" : "<...>";
} catch (x) {
return "<...>";
}
}
function getOwner() {
var dispatcher = ReactSharedInternals.A;
return null === dispatcher ? null : dispatcher.getOwner();
}
function UnknownOwner() {
return Error("react-stack-top-frame");
}
function hasValidKey(config) {
if (hasOwnProperty.call(config, "key")) {
var getter = Object.getOwnPropertyDescriptor(config, "key").get;
if (getter && getter.isReactWarning) return !1;
}
return void 0 !== config.key;
}
function defineKeyPropWarningGetter(props, displayName) {
function warnAboutAccessingKey() {
specialPropKeyWarningShown ||
((specialPropKeyWarningShown = !0),
console.error(
"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
displayName
));
}
warnAboutAccessingKey.isReactWarning = !0;
Object.defineProperty(props, "key", {
get: warnAboutAccessingKey,
configurable: !0
});
}
function elementRefGetterWithDeprecationWarning() {
var componentName = getComponentNameFromType(this.type);
didWarnAboutElementRef[componentName] ||
((didWarnAboutElementRef[componentName] = !0),
console.error(
"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
));
componentName = this.props.ref;
return void 0 !== componentName ? componentName : null;
}
function ReactElement(type, key, props, owner, debugStack, debugTask) {
var refProp = props.ref;
type = {
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
props: props,
_owner: owner
};
null !== (void 0 !== refProp ? refProp : null)
? Object.defineProperty(type, "ref", {
enumerable: !1,
get: elementRefGetterWithDeprecationWarning
})
: Object.defineProperty(type, "ref", { enumerable: !1, value: null });
type._store = {};
Object.defineProperty(type._store, "validated", {
configurable: !1,
enumerable: !1,
writable: !0,
value: 0
});
Object.defineProperty(type, "_debugInfo", {
configurable: !1,
enumerable: !1,
writable: !0,
value: null
});
Object.defineProperty(type, "_debugStack", {
configurable: !1,
enumerable: !1,
writable: !0,
value: debugStack
});
Object.defineProperty(type, "_debugTask", {
configurable: !1,
enumerable: !1,
writable: !0,
value: debugTask
});
Object.freeze && (Object.freeze(type.props), Object.freeze(type));
return type;
}
function jsxDEVImpl(
type,
config,
maybeKey,
isStaticChildren,
debugStack,
debugTask
) {
var children = config.children;
if (void 0 !== children)
if (isStaticChildren)
if (isArrayImpl(children)) {
for (
isStaticChildren = 0;
isStaticChildren < children.length;
isStaticChildren++
)
validateChildKeys(children[isStaticChildren]);
Object.freeze && Object.freeze(children);
} else
console.error(
"React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
);
else validateChildKeys(children);
if (hasOwnProperty.call(config, "key")) {
children = getComponentNameFromType(type);
var keys = Object.keys(config).filter(function (k) {
return "key" !== k;
});
isStaticChildren =
0 < keys.length
? "{key: someKey, " + keys.join(": ..., ") + ": ...}"
: "{key: someKey}";
didWarnAboutKeySpread[children + isStaticChildren] ||
((keys =
0 < keys.length ? "{" + keys.join(": ..., ") + ": ...}" : "{}"),
console.error(
'A props object containing a "key" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />',
isStaticChildren,
children,
keys,
children
),
(didWarnAboutKeySpread[children + isStaticChildren] = !0));
}
children = null;
void 0 !== maybeKey &&
(checkKeyStringCoercion(maybeKey), (children = "" + maybeKey));
hasValidKey(config) &&
(checkKeyStringCoercion(config.key), (children = "" + config.key));
if ("key" in config) {
maybeKey = {};
for (var propName in config)
"key" !== propName && (maybeKey[propName] = config[propName]);
} else maybeKey = config;
children &&
defineKeyPropWarningGetter(
maybeKey,
"function" === typeof type
? type.displayName || type.name || "Unknown"
: type
);
return ReactElement(
type,
children,
maybeKey,
getOwner(),
debugStack,
debugTask
);
}
function validateChildKeys(node) {
isValidElement(node)
? node._store && (node._store.validated = 1)
: "object" === typeof node &&
null !== node &&
node.$$typeof === REACT_LAZY_TYPE &&
("fulfilled" === node._payload.status
? isValidElement(node._payload.value) &&
node._payload.value._store &&
(node._payload.value._store.validated = 1)
: node._store && (node._store.validated = 1));
}
function isValidElement(object) {
return (
"object" === typeof object &&
null !== object &&
object.$$typeof === REACT_ELEMENT_TYPE
);
}
var React$1 = React,
REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"),
REACT_PORTAL_TYPE = Symbol.for("react.portal"),
REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"),
REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode"),
REACT_PROFILER_TYPE = Symbol.for("react.profiler"),
REACT_CONSUMER_TYPE = Symbol.for("react.consumer"),
REACT_CONTEXT_TYPE = Symbol.for("react.context"),
REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref"),
REACT_SUSPENSE_TYPE = Symbol.for("react.suspense"),
REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list"),
REACT_MEMO_TYPE = Symbol.for("react.memo"),
REACT_LAZY_TYPE = Symbol.for("react.lazy"),
REACT_ACTIVITY_TYPE = Symbol.for("react.activity"),
REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference"),
ReactSharedInternals =
React$1.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
hasOwnProperty = Object.prototype.hasOwnProperty,
isArrayImpl = Array.isArray,
createTask = console.createTask
? console.createTask
: function () {
return null;
};
React$1 = {
react_stack_bottom_frame: function (callStackForError) {
return callStackForError();
}
};
var specialPropKeyWarningShown;
var didWarnAboutElementRef = {};
var unknownOwnerDebugStack = React$1.react_stack_bottom_frame.bind(
React$1,
UnknownOwner
)();
var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));
var didWarnAboutKeySpread = {};
reactJsxRuntime_development$1.Fragment = REACT_FRAGMENT_TYPE;
reactJsxRuntime_development$1.jsx = function (type, config, maybeKey) {
var trackActualOwner =
1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
return jsxDEVImpl(
type,
config,
maybeKey,
!1,
trackActualOwner
? Error("react-stack-top-frame")
: unknownOwnerDebugStack,
trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask
);
};
reactJsxRuntime_development$1.jsxs = function (type, config, maybeKey) {
var trackActualOwner =
1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;
return jsxDEVImpl(
type,
config,
maybeKey,
!0,
trackActualOwner
? Error("react-stack-top-frame")
: unknownOwnerDebugStack,
trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask
);
};
})();
return reactJsxRuntime_development$1;
}
if (process.env.NODE_ENV === 'production') {
jsxRuntime$1.exports = requireReactJsxRuntime_production$1();
} else {
jsxRuntime$1.exports = requireReactJsxRuntime_development$1();
}
var jsxRuntimeExports$1 = jsxRuntime$1.exports;
const DfFormErrorMsg$1 = ({ validationErrors, fieldId, touchedFields, formSubmitted, properties, localValidation, isTouched: localIsTouched, mode, }) => {
const getErrorMessage = () => {
// Only show error messages in test mode
if (mode !== 'test') {
return '';
}
// Check if field is touched or form is submitted
const isTouched = localIsTouched || touchedFields[fieldId] || formSubmitted;
if (!isTouched) {
return '';
}
// Prioritize local validation - if local validation is valid, don't show any errors
if (localValidation && localValidation.isValid) {
return '';
}
// Show local validation errors if validation failed
if (localValidation && !localValidation.isValid && Object.keys(localValidation.errors).length > 0) {
const errorKeys = Object.keys(localValidation.errors);
if (errorKeys.length > 0) {
const firstError = errorKeys[0];
return getDefaultErrorMessage(firstError);
}
}
// Only check external validation errors if local validation doesn't exist or is not valid
const fieldErrors = validationErrors[fieldId];
if (fieldErrors && typeof fieldErrors === 'string' && fieldErrors.trim() !== '') {
// For date/time components, check if we should use mode-specific message
const componentName = properties.name || '';
const isDateTimeComponent = componentName === 'date-picker' || componentName === 'datetime-picker';
if (isDateTimeComponent && fieldErrors.includes('valid date')) {
const dateTimeMode = properties.basic?.dateTimeMode || (componentName === 'datetime-picker' ? 'datetime' : 'date');
if (dateTimeMode === 'time') {
return 'Please select a valid time';
}
else if (dateTimeMode === 'datetime') {
return 'Please select a valid date and time';
}
}
return fieldErrors;
}
return '';
};
const getDefaultErrorMessage = (errorType) => {
const fieldLabel = properties.basic.label || 'This field';
// Check if there's a custom validation message
const customMessage = properties.validation.customValidationMessage;
if (customMessage) {
return customMessage;
}
// For date/time components, check dateTimeMode for appropriate error messages
const componentName = properties.name || '';
const isDateTimeComponent = componentName === 'date-picker' || componentName === 'datetime-picker';
const dateTimeMode = isDateTimeComponent ? (properties.basic?.dateTimeMode || (componentName === 'datetime-picker' ? 'datetime' : 'date')) : null;
switch (errorType) {
case 'required':
return `${fieldLabel} is required`;
case 'invalidDate':
// Show mode-specific error messages for date/time components
if (dateTimeMode === 'time') {
return 'Please select a valid time';
}
else if (dateTimeMode === 'datetime') {
return 'Please select a valid date and time';
}
else if (dateTimeMode === 'date') {
return 'Please select a valid date';
}
return `${fieldLabel} is invalid`;
case 'minLength':
const minLength = properties.validation.minLength;
return `${fieldLabel} must be at least ${minLength} characters long`;
case 'maxLength':
const maxLength = properties.validation.maxLength;
return `${fieldLabel} must be no more than ${maxLength} characters long`;
case 'min':
const min = properties.validation.min;
return `${fieldLabel} must be at least ${min}`;
case 'max':
const max = properties.validation.max;
return `${fieldLabel} must be no more than ${max}`;
case 'pattern':
return `${fieldLabel} format is invalid`;
case 'email':
return `${fieldLabel} must be a valid email address`;
default:
return `${fieldLabel} is invalid`;
}
};
const errorMessage = getErrorMessage();
if (!errorMessage) {
return null;
}
return (jsxRuntimeExports$1.jsx("div", { className: "form-error-msg", children: errorMessage }));
};
const DfFormInput$1 = ({ id, properties, validationErrors = {}, formValue = '', inputType = 'text', readonly = false, disabled = false, touchedFields = {}, formSubmitted = false, mode = 'test', onValueChange, onBlur, onFocus, className = '', hideLabel = false }) => {
const [value, setValue] = React.useState(formValue || properties?.basic?.defaultValue || '');
const [isTouched, setIsTouched] = React.useState(false);
const [isFocused, setIsFocused] = React.useState(false);
// Determine input type based on component name
const getInputType = () => {
if (inputType)
return inputType;
const name = properties?.name;
if (name === 'text-input')
return 'text';
if (name === 'number-input')
return 'number';
if (name === 'email-input')
return 'email';
return 'text';
};
// Build validation rules
const buildValidationRules = () => {
const rules = {};
if (properties?.validation?.required) {
rules.required = true;
}
if (getInputType() === 'email') {
rules.email = true;
}
if (getInputType() === 'text') {
const textValidation = properties.validation;
if (textValidation?.minLength)
rules.minLength = textValidation.minLength;
if (textValidation?.maxLength)
rules.maxLength = textValidation.maxLength;
}
if (getInputType() === 'number') {
const numberValidation = properties.validation;
if (numberValidation?.min)
rules.min = numberValidation.min;
if (numberValidation?.max)
rules.max = numberValidation.max;
if (numberValidation?.minLength)
rules.minLength = numberValidation.minLength;
if (numberValidation?.maxLength)
rules.maxLength = numberValidation.maxLength;
}
return rules;
};
// Validate field
const validateField = React.useCallback((val) => {
const rules = buildValidationRules();
const errors = {};
let isValid = true;
// Required validation
if (rules.required && (!val || val.trim() === '')) {
errors.required = true;
isValid = false;
}
// Email validation
if (rules.email && val && !/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(val)) {
errors.email = true;
isValid = false;
}
// Length validations
if (rules.minLength && val && val.length < rules.minLength) {
errors.minLength = true;
isValid = false;
}
if (rules.maxLength && val && val.length > rules.maxLength) {
errors.maxLength = true;
isValid = false;
}
// Number validations
if (getInputType() === 'number' && val) {
const numValue = parseFloat(val);
if (isNaN(numValue)) {
errors.pattern = true;
isValid = false;
}
else {
if (rules.min !== undefined && numValue < rules.min) {
errors.min = true;
isValid = false;
}
if (rules.max !== undefined && numValue > rules.max) {
errors.max = true;
isValid = false;
}
}
}
return { isValid, errors };
}, [properties, getInputType]);
// Handle value changes
const handleValueChange = React.useCallback((newValue) => {
setValue(newValue);
if (onValueChange) {
const validation = validateField(newValue);
const change = {
id,
value: newValue,
isValid: validation.isValid,
errors: validation.errors,
};
onValueChange(change);
}
}, [id, onValueChange, validateField]);
// Handle input change
const handleInputChange = (e) => {
const newValue = e.target.value;
handleValueChange(newValue);
};
// Handle focus
const handleFocus = () => {
setIsFocused(true);
if (onFocus) {
onFocus();
}
};
// Handle blur
const handleBlur = () => {
setIsFocused(false);
setIsTouched(true);
// Always trigger validation on blur in test mode for any field with validation rules
if (mode === 'test' && onValueChange) {
const validation = validateField(value);
const change = {
id,
value: value,
isValid: validation.isValid,
errors: validation.errors,
};
onValueChange(change);
}
if (onBlur) {
onBlur();
}
};
// Update touched fields
React.useEffect(() => {
if (isTouched) {
touchedFields[id] = true;
}
}, [isTouched, id, touchedFields]);
// Reset touched state and value when switching modes
React.useEffect(() => {
if (mode === 'edit') {
setIsTouched(false);
// Reset value to default value when switching to edit mode
const defaultValue = properties?.basic?.defaultValue || '';
setValue(defaultValue);
}
else if (mode === 'test') {
setIsTouched(false);
// Reset value to empty when switching to test mode for fresh start
setValue('');
}
}, [mode, properties?.basic?.defaultValue]);
// Update value when formValue prop changes (but don't override user input)
React.useEffect(() => {
const newValue = formValue || properties?.basic?.defaultValue || '';
// Only update if the new value is different and we're not currently focused
// Also, don't override with empty value if user has typed something
// CRITICAL FIX: Don't reset user input when formValue becomes empty
if (newValue !== value && !isFocused && !(newValue === '' && value.trim() !== '')) {
setValue(newValue);
}
else if (newValue === '' && value.trim() !== '' && !isFocused) ;
}, [formValue, value, isFocused, properties?.basic?.defaultValue]);
// Mark as touched when form is submitted
React.useEffect(() => {
if (formSubmitted) {
setIsTouched(true);
}
}, [formSubmitted]);
// Clear value when switching away from test mode
React.useEffect(() => {
if (mode !== 'test') {
setValue('');
}
}, [mode]);
// Get label alignment class
const getLabelAlignmentClass = () => {
const alignment = properties?.styles?.labelAlignment;
return alignment === 'left' ? 'label-left' : 'label-top';
};
// Get input classes
const getInputClasses = () => {
const base = 'form-control';
// Only show validation errors in test mode
if (mode === 'test') {
const validation = validateField(value);
const hasLocalError = !validation.isValid && isTouched;
const hasExternalError = validationErrors[id] && (isTouched || formSubmitted);
const error = (hasLocalError || hasExternalError) ? 'is-invalid' : '';
return `${base} ${error} ${className}`.trim();
}
return `${base} ${className}`.trim();
};
const currentInputType = getInputType();
const shouldDisable = disabled || readonly || properties?.validation?.readonly;
return (jsxRuntimeExports$1.jsxs("div", { className: `form-group ${getLabelAlignmentClass()}`, children: [!hideLabel && properties.basic.label && (jsxRuntimeExports$1.jsxs("label", { htmlFor: id, className: "form-label", children: [properties.basic.label, properties.validation.required && (jsxRuntimeExports$1.jsx("span", { className: "required-indicator", children: "*" }))] })), jsxRuntimeExports$1.jsxs("div", { className: "input-wrapper", children: [jsxRuntimeExports$1.jsx("input", { type: currentInputType, id: id, value: value, onChange: handleInputChange, onFocus: handleFocus, onBlur: handleBlur, placeholder: properties.basic.placeholder, disabled: shouldDisable, readOnly: readonly || properties?.validation?.readonly, className: getInputClasses() }), jsxRuntimeExports$1.jsx(DfFormErrorMsg$1, { validationErrors: validationErrors, fieldId: id, touchedFields: touchedFields, formSubmitted: formSubmitted, properties: properties, localValidation: validateField(value), isTouched: isTouched, mode: mode })] })] }));
};
const DfFormTextarea = ({ id, properties, validationErrors = {}, formValue = '', readonly = false, disabled = false, touchedFields = {}, formSubmitted = false, mode = 'preview', onValueChange, onBlur, onFocus, className = '', hideLabel = false }) => {
const [value, setValue] = React.useState(formValue);
const [isTouched, setIsTouched] = React.useState(false);
// Validate field
const validateField = React.useCallback((val) => {
const errors = {};
let isValid = true;
// Required validation
if (properties?.validation?.required && (!val || val.trim() === '')) {
errors.required = true;
isValid = false;
}
// Length validations
if (properties?.validation?.minLength && val && val.length < properties.validation.minLength) {
errors.minLength = true;
isValid = false;
}
if (properties?.validation?.maxLength && val && val.length > properties.validation.maxLength) {
errors.maxLength = true;
isValid = false;
}
return { isValid, errors };
}, [properties]);
// Handle value changes
const handleValueChange = React.useCallback((newValue) => {
setValue(newValue);
if (onValueChange) {
const validation = validateField(newValue);
const change = {
id,
value: newValue,
isValid: validation.isValid,
errors: validation.errors,
};
onValueChange(change);
}
}, [id, onValueChange, validateField]);
// Handle textarea change
const handleTextareaChange = (e) => {
const newValue = e.target.value;
handleValueChange(newValue);
};
// Handle focus
const handleFocus = () => {
if (onFocus) {
onFocus();
}
};
// Handle blur
const handleBlur = () => {
setIsTouched(true);
if (onBlur) {
onBlur();
}
};
// Update touched fields
React.useEffect(() => {
if (isTouched) {
touchedFields[id] = true;
}
}, [isTouched, id, touchedFields]);
// Update value when formValue prop changes (only on initial load or when component is reset)
React.useEffect(() => {
// Only update if the current value is empty (initial state) or if formValue is explicitly set
// This prevents the component from resetting user input when they blur the field
if (value === '' && formValue !== '') {
setValue(formValue);
}
}, [formValue]); // Remove value and isFocused from dependencies to prevent reset on blur
// Mark as touched when form is submitted
React.useEffect(() => {
if (formSubmitted) {
setIsTouched(true);
}
}, [formSubmitted]);
// Clear value when switching away from test mode
React.useEffect(() => {
if (mode !== 'test') {
setValue('');
}
}, [mode]);
// Get label alignment class
const getLabelAlignmentClass = () => {
const alignment = properties?.styles?.labelAlignment;
return alignment === 'left' ? 'label-left' : 'label-top';
};
// Get textarea classes
const getTextareaClasses = () => {
const base = 'form-textarea';
const validation = validateField(value);
const error = !validation.isValid && isTouched ? 'is-invalid' : '';
return `${base} ${error} ${className}`.trim();
};
const shouldDisable = disabled || readonly || properties?.validation?.readonly;
const rows = properties?.validation?.rows || 4;
return (jsxRuntimeExports$1.jsxs("div", { className: `form-group ${getLabelAlignmentClass()}`, children: [!hideLabel && properties.basic.label && (jsxRuntimeExports$1.jsxs("label", { htmlFor: id, className: "form-label", children: [properties.basic.label, properties.validation.required && (jsxRuntimeExports$1.jsx("span", { className: "required-indicator", children: "*" }))] })), jsxRuntimeExports$1.jsxs("div", { className: "input-wrapper", children: [jsxRuntimeExports$1.jsx("textarea", { id: id, value: value, onChange: handleTextareaChange, onFocus: handleFocus, onBlur: handleBlur, placeholder: properties.basic.placeholder, disabled: shouldDisable, readOnly: readonly || properties?.validation?.readonly, rows: rows, className: getTextareaClasses() }), jsxRuntimeExports$1.jsx(DfFormErrorMsg$1, { validationErrors: validationErrors, fieldId: id, touchedFields: touchedFields, formSubmitted: formSubmitted, properties: properties, localValidation: validateField(value), isTouched: isTouched, mode: mode })] })] }));
};
const DfFormComments = ({ comment = '', onSave, placeholder = 'Enter your reason...', className = '', disabled = false }) => {
const [isExpanded, setIsExpanded] = React.useState(true);
const [currentComment, setCurrentComment] = React.useState(comment);
const [hasChanges, setHasChanges] = React.useState(false);
// Update local state when comment prop changes
React.useEffect(() => {
setCurrentComment(comment);
setHasChanges(false);
}, [comment]);
// Handle input changes
const handleInputChange = React.useCallback((event) => {
const newValue = event.target.value;
setCurrentComment(newValue);
setHasChanges(newValue !== comment);
}, [comment]);
// Handle input blur with auto-save
const handleInputBlur = React.useCallback(() => {
if (hasChanges && onSave && !disabled) {
onSave(currentComment);
setHasChanges(false);
}
}, [hasChanges, onSave, currentComment, disabled]);
// Handle input focus
const handleInputFocus = React.useCallback(() => {
// Focus handler if needed
}, []);
// Toggle comments section
const toggleComments = React.useCallback(() => {
if (!disabled) {
setIsExpanded(!isExpanded);
}
}, [isExpanded, disabled]);
return (jsxRuntimeExports$1.jsxs("div", { className: `df-form-comments ${className}`, children: [jsxRuntimeExports$1.jsxs("div", { className: "df-form-comments__header", children: [jsxRuntimeExports$1.jsx("h3", { className: "df-form-comments__title", children: "Comments" }), jsxRuntimeExports$1.jsx("button", { className: "df-form-comments__toggle", type: "button", onClick: toggleComments, "aria-expanded": isExpanded, "aria-label": "Toggle comments section", disabled: disabled, children: isExpanded ? (jsxRuntimeExports$1.jsx("span", { className: "df-form-comments__toggle-icon", children: "\u25BC" })) : (jsxRuntimeExports$1.jsx("span", { className: "df-form-comments__toggle-icon", children: "\u25B6" })) })] }), jsxRuntimeExports$1.jsx("div", { className: `df-form-comments__content ${isExpanded ? 'df-form-comments__content--expanded' : ''}`, children: jsxRuntimeExports$1.jsx("div", { className: "df-form-comments__input-container", children: jsxRuntimeExports$1.jsx("div", { className: "df-form-comments__input-line", children: jsxRuntimeExports$1.jsx("input", { type: "text", id: "comment-input", className: "df-form-comments__input", value: currentComment, onChange: handleInputChange, onBlur: handleInputBlur, onFocus: handleInputFocus, placeholder: placeholder, disabled: disabled }) }) }) })] }));
};
const DfFormSelect = ({ id, properties, validationErrors = {}, formValue = '', readonly = false, disabled = false, touchedFields = {}, formSubmitted = false, mode = 'preview', onValueChange, onBlur, onFocus, className = '', hideLabel = false, showCommentsInPreview = false }) => {
const [selectedValue, setSelectedValue] = React.useState(formValue);
const [isTouched, setIsTouched] = React.useState(false);
const [comment, setComment] = React.useState('');
const [showCommentInput, setShowCommentInput] = React.useState(false);
// Check if multiple selection is enabled
const isMultiple = properties?.validation?.multiple || false;
// Initialize comment from properties when in preview mode (for form submissions)
React.useEffect(() => {
if (mode === 'preview' && showCommentsInPreview && properties?.basic?.comments) {
setComment(properties.basic.comments);
}
}, [mode, showCommentsInPreview, properties]);
// Validate field
const validateField = React.useCallback((value) => {
const errors = {};
let isValid = true;
// Required validation
if (properties?.validation?.required) {
if (isMultiple) {
if (!Array.isArray(value) || value.length === 0) {
errors.required = true;
isValid = false;
}
}
else {
if (!value || (typeof value === 'string' && value.trim() === '')) {
errors.required = true;
isValid = false;
}
}
}
return { isValid, errors };
}, [properties, isMultiple]);
// Handle value changes
const handleValueChange = React.useCallback((newValue) => {
setSelectedValue(newValue);
if (onValueChange) {
const validation = validateField(newValue);
const change = {
id,
value: newValue,
isValid: validation.isValid,
errors: validation.errors,
comments: comment
};
onValueChange(change);
}
}, [id, onValueChange, validateField, comment]);
// Helper function to check if a value should show red chip (fail/no)
const shouldShowRedChip = (value) => {
const lowerValue = value.toLowerCase();
return lowerValue === 'no' || lowerValue === 'fail' || lowerValue === 'false';
};
// Check if comments section should be shown
const shouldShowCommentsSection = () => {
if (isMultiple) {
return Array.isArray(selectedValue) && selectedValue.some(value => shouldShowRedChip(value));
}
else {
return typeof selectedValue === 'string' && shouldShowRedChip(selectedValue);
}
};
// Check if comment input should be shown
const shouldShowCommentInput = () => {
// Don't show comment input in preview mode
if (mode === 'preview') {
return false;
}
const needsComments = shouldShowCommentsSection();
const hasNoComment = !comment || comment.trim() === '';
return needsComments && (showCommentInput || hasNoComment);
};
// Check if comment display should be shown
const shouldShowCommentDisplay = () => {
// In preview mode, show comments if they exist and showCommentsInPreview is true
if (mode === 'preview' && showCommentsInPreview && comment) {
return true;
}
return shouldShowCommentsSection() && !showCommentInput && !!comment;
};
// Toggle comment input visibility
const toggleCommentInput = () => {
// Don't allow toggling in preview mode
if (mode === 'preview') {
return;
}
setShowCommentInput(!showCommentInput);
};
// Handle comment save
const handleCommentSave = (newComment) => {
setComment(newComment);
setShowCommentInput(false);
if (onValueChange) {
const validation = validateField(selectedValue);
const change = {
id,
value: selectedValue,
isValid: validation.isValid,
errors: validation.errors,
comments: newComment
};
onValueChange(change);
}
};
// Handle select change
const handleSelectChange = (e) => {
if (isMultiple) {
const selectedOptions = Array.from(e.target.selectedOptions, option => option.value);
handleValueChange(selectedOptions);
}
else {
handleValueChange(e.target.value);
}
};
// Handle focus
const handleFocus = () => {
if (onFocus) {
onFocus();
}
};
// Handle blur
const handleBlur = () => {
setIsTouched(true);
if (onBlur) {
onBlur();
}
};
// Update touched fields
React.useEffect(() => {
if (isTouched) {
touchedFields[id] = true;
}
}, [isTouched, id, touchedFields]);
// Update value when formValue prop changes
React.useEffect(() => {
if (JSON.stringify(formValue) !== JSON.stringify(selectedValue)) {
setSelectedValue(formValue);
}
}, [formValue, selectedValue]);
// Mark as touched when form is submitted
React.useEffect(() => {
if (formSubmitted) {
setIsTouched(true);
}
}, [formSubmitted]);
// Get label alignment class
const getLabelAlignmentClass = () => {
const alignment = properties?.styles?.labelAlignment;
return alignment === 'left' ? 'label-left' : 'label-top';
};
// Get select classes
const getSelectClasses = () => {
const base = 'form-select';
const validation = validateField(selectedValue);
const error = !validation.isValid && isTouched ? 'is-invalid' : '';
return `${base} ${error} ${className}`.trim();
};
const shouldDisable = disabled || readonly || properties?.validation?.readonly;
return (jsxRuntimeExports$1.jsxs("div", { className: `form-group ${getLabelAlignmentClass()}`, children: [!hideLabel && properties.basic.label && (jsxRuntimeExports$1.jsxs("label", { htmlFor: id, className: "form-label", children: [properties.basic.label, properties.validation.required && (jsxRuntimeExports$1.jsx("span", { className: "required-indicator", children: "*" }))] })), jsxRuntimeExports$1.jsxs("div", { className: "input-wrapper", children: [jsxRuntimeExports$1.jsxs("select", { id: id, value: isMultiple ? selectedValue : selectedValue, onChange: handleSelectChange, onFocus: handleFocus, onBlur: handleBlur, disabled: shouldDisable, multiple: isMultiple, className: getSelectClasses(), children: [!isMultiple && (jsxRuntimeExports$1.jsx("option", { value: "", children: properties.basic.placeholder || 'Select an option' })), (properties.options || []).map((option, index) => (jsxRuntimeExports$1.jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, index)))] }), jsxRuntimeExports$1.jsx(DfFormErrorMsg$1, { validationErrors: validationErrors, fieldId: id, touchedFields: touchedFields, formSubmitted: formSubmitted, properties: properties, localValidation: validateField(selectedValue), isTouched: isTouched, mode: mode }), (!shouldDisable || (mode === 'preview' && showCommentsInPreview)) && (jsxRuntimeExports$1.jsxs(jsxRuntimeExports$1.Fragment, { children: [shouldShowCommentDisplay() && (jsxRuntimeExports$1.jsx("div", { onClick: mode === 'preview' ? undefined : toggleCommentInput, style: { cursor: mode === 'preview' ? 'default' : 'pointer' }, children: jsxRuntimeExports$1.jsxs("label", { className: "form-label notes-label-text", children: ["Notes: ", comment] }) })), shouldShowCommentInput() && (jsxRuntimeExports$1.jsx(DfFormComments, { comment: comment, onSave: handleCommentSave, placeholder: "Enter your reason...", disabled: shouldDisable }))] }))] })] }));
};
const DfFormCheckbox = ({ id, properties, validationErrors = {}, formValue = [], readonly = false, disabled = false, touchedFields = {}, formSubmitted = false, mode = 'preview', onValueChange, onBlur, onFocus, className = '', hideLabel = false, showCommentsInPreview = false }) => {
const [isTouched, setIsTouched] = React.useState(false);
const [comment, setComment] = React.useState('');
const [showCommentInput, setShowCommentInput] = React.useState(false);
// Use formValue directly instead of local state to avoid conflicts
const selectedValues = Array.isArray(formValue) ? formValue : [];
// Initialize comment from properties when in preview mode (for form submissions)
React.useEffect(() => {
if (mode === 'preview' && showCommentsInPreview && properties?.basic?.comments) {
setComment(properties.basic.comments);
}
}, [mode, showCommentsInPreview, properties]);
// Validate field
const validateField = React.useCallback((values) => {
const errors = {};
let isValid = true;
// Required validation
if (properties?.validation?.required && values.length === 0) {
errors.required = true;
isValid = false;
}
return { isValid, errors };
}, [properties]);
// Handle value changes
const handleValueChange = React.useCallback((newValues) => {
if (onValueChange) {
const validation = validateField(newValues);
const change = {
id,
value: newValues,
isValid: validation.isValid,
errors: validation.errors,
comments: comment
};
onValueChange(change);
}
}, [id, onValueChange, validateField, comment, selectedValues]);
// Helper function to check if a value should show red chip (fail/no)
const shouldShowRedChip = (value) => {
const lowerValue = value.toLowerCase();
return lowerValue === 'no' || lowerValue === 'fail' || lowerValue === 'false';
};
// Check if comments section should be shown
const shouldShowCommentsSection = () => {
return selectedValues.some(value => shouldShowRedChip(value));
};
// Check if comment input should be shown
const shouldShowCommentInput = () => {
// Don't show comment input in preview mode
if (mode === 'preview') {
return false;
}
const needsComments = shouldShowCommentsSection();
const hasNoComment = !comment || comment.trim() === '';
return needsComments && (showCommentInput || hasNoComment);
};
// Check if comment display should be shown
const shouldShowCommentDisplay = () => {
// In preview mode, show comments if they exist and showCommentsInPreview is true
if (mode === 'preview' && showCommentsInPreview && comment) {
return true;
}
return shouldShowCommentsSection() && !showCommentInput && !!comment;
};
// Toggle comment input visibility
const toggleCommentInput = () => {
// Don't allow toggling in preview mode
if (mode === 'preview') {
return;
}
setShowCommentInput(!showCommentInput);
};
// Handle comment save
const handleCommentSave = (newComment) => {
setComment(newComment);
setShowCommentInput(false);
if (onValueChange) {
const validation = validateField(selectedValues);
const change = {
id,
value: selectedValues,
isValid: validation.isValid,
errors: validation.errors,
comments: newComment
};
onValueChange(change);
}
};
// Handle checkbox change
const handleCheckboxChange = (optionValue, checked) => {
let newValues;
if (checked) {
newValues = [...selectedValues, optionValue];
}
else {
newValues = selectedValues.filter(value => value !== optionValue);
}
handleValueChange(newValues);
};
// Handle focus
const handleFocus = () => {
if (onFocus) {
onFocus();
}
};
// Handle blur
const handleBlur = () => {
setIsTouched(true);
if (onBlur) {
onBlur();
}
};
// Update touched fields
React.useEffect(() => {
if (isTouched) {
touchedFields[id] = true;
}
}, [isTouched, id, touchedFields]);
// Log formValue changes for debugging
React.useEffect(() => {
}, [formValue, id, selectedValues]);
// Mark as touched when form is submitted
React.useEffect(() => {
if (formSubmitted) {
setIsTouched(true);
}
}, [formSubmitted]);
// Get label alignment class
const getLabelAlignmentClass = () => {
const alignment = properties?.styles?.labelAlignment;
return alignment === 'left' ? 'label-left' : 'label-top';
};
// Get layout class for inline/vertical layout
const getLayoutClass = () => {
return properties?.basic?.inlineLayout ? 'inline-layout' : 'vertical-layout';
};
const shouldDisable = disabled || readonly || properties?.validation?.readonly;
return (jsxRuntimeExports$1.jsxs(jsxRuntimeExports$1.Fragment, { children: [jsxRuntimeExports$1.jsx("div", { className: `form-group checkbox-group ${getLabelAlignmentClass()} ${getLayoutClass()} ${className}`, children: !hideLabel && properties.basic.label ? (jsxRuntimeExports$1.jsxs(jsxRuntimeExports$1.Fragment, { children: [jsxRuntimeExports$1.jsxs("label", { className: "form-label", children: [properties.basic.label, properties.validation.required && (jsxRuntimeExports$1.jsx("span", { className: "required-indicator", children: "*" }))] }), jsxRuntimeExports$1.jsxs("div", { className: "input-wrapper", children: [jsxRuntimeExports$1.jsx("div", { className: `mt-2 ${properties?.basic?.inlineLayout ? 'inline-options-grid' : 'space-y-2'}`, children: (properties.options || []).map((option, index) => (jsxRuntimeExports$1.jsxs("div", { className: "form-check", children: [jsxRuntimeExports$1.jsx("input", { type: "checkbox", id: `${id}-${index}`, name: id, value: option.value, checked: selectedValues.inc