@conform-to/react
Version:
Conform view adapter for react
801 lines (774 loc) • 32.2 kB
JavaScript
'use client';
;
Object.defineProperty(exports, '__esModule', { value: true });
var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js');
var future = require('@conform-to/dom/future');
var react = require('react');
var util = require('./util.js');
var state = require('./state.js');
var intent = require('./intent.js');
var dom = require('./dom.js');
var reactDom = require('react-dom');
var jsxRuntime = require('react/jsx-runtime');
var _excluded = ["name", "form", "defaultValue", "hidden"],
_excluded2 = ["defaultValue", "multiple", "hidden"],
_excluded3 = ["defaultValue", "hidden"],
_excluded4 = ["defaultValue", "value", "hidden"],
_excluded5 = ["defaultValue", "hidden"];
var INITIAL_KEY = 'INITIAL_KEY';
var GlobalFormsObserverContext = /*#__PURE__*/react.createContext(future.createGlobalFormsObserver());
/**
* Preserves form field values when its contents are unmounted.
* Useful for multi-step forms and virtualized lists.
*
* See https://conform.guide/api/react/future/PreserveBoundary
*/
function PreserveBoundary(props) {
// name is used as key so React properly unmounts/remounts when switching
// between boundaries. Without it, both sides of a ternary share
// key={undefined} and React reuses the instance (useId and key prop
// can't help here). This is why name is required.
return /*#__PURE__*/jsxRuntime.jsx(PreserveBoundaryImpl, _rollupPluginBabelHelpers.objectSpread2({}, props), props.name);
}
function PreserveBoundaryImpl(props) {
var fieldsetRef = react.useRef(null);
// useLayoutEffect to restore values before paint, avoiding flash of default values
useSafeLayoutEffect(() => {
var fieldset = fieldsetRef.current;
if (!fieldset || !fieldset.form) {
return;
}
var form = fieldset.form;
// On mount: restore values from preserved inputs
dom.cleanupPreservedInputs(fieldset, form, props.name);
return () => {
// On unmount: preserve input values
dom.preserveInputs(fieldset.querySelectorAll('input,select,textarea'), form, props.name);
};
}, [props.name]);
return /*#__PURE__*/jsxRuntime.jsx("fieldset", {
ref: fieldsetRef,
form: props.form,
style: {
display: 'contents'
},
children: props.children
});
}
/**
* Core form hook that manages form state, validation, and submission.
* Handles both sync and async validation, intent dispatching, and DOM updates.
*/
function useConform(formRef, options) {
var {
lastResult
} = options;
var [state$1, setState] = react.useState(() => {
var state$1 = state.initializeState({
defaultValue: options.defaultValue,
resetKey: INITIAL_KEY
});
if (lastResult) {
var intent$1 = lastResult.submission.intent ? intent.deserializeIntent(lastResult.submission.intent) : null;
var result = intent.applyIntent(lastResult, intent$1, {
handlers: intent.intentHandlers
});
state$1 = state.updateState(state$1, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
type: 'initialize',
intent: intent$1,
ctx: {
handlers: intent.intentHandlers,
cancelled: result !== lastResult,
reset: defaultValue => state.initializeState({
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue,
resetKey: INITIAL_KEY
})
}
}));
}
return state$1;
});
var keyRef = react.useRef(options.key);
var resetKeyRef = react.useRef(state$1.resetKey);
var optionsRef = useLatest(options);
var lastResultRef = react.useRef(lastResult);
var pendingValueRef = react.useRef();
var lastAsyncResultRef = react.useRef(null);
var abortControllerRef = react.useRef(null);
var handleSubmission = react.useCallback(function (type, result) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : optionsRef.current;
var normalizedResult = !result.error ? result : _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, result), {}, {
error: future.normalizeFormError(result.error)
});
var intent$1 = result.submission.intent ? intent.deserializeIntent(result.submission.intent) : null;
var finalResult = intent.applyIntent(normalizedResult, intent$1, {
handlers: intent.intentHandlers
});
var formElement = dom.getFormElement(formRef);
if (formElement && (finalResult.reset || typeof finalResult.targetValue !== 'undefined')) {
future.dispatchInternalUpdateEvent(formElement);
}
setState(state$1 => state.updateState(state$1, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, finalResult), {}, {
type,
intent: intent$1,
ctx: {
handlers: intent.intentHandlers,
cancelled: finalResult !== normalizedResult,
reset(defaultValue) {
return state.initializeState({
defaultValue: defaultValue !== null && defaultValue !== void 0 ? defaultValue : options.defaultValue
});
}
}
})));
// TODO: move on error handler to a new effect
if (formElement && finalResult.error) {
var _optionsRef$current$o, _optionsRef$current;
(_optionsRef$current$o = (_optionsRef$current = optionsRef.current).onError) === null || _optionsRef$current$o === void 0 || _optionsRef$current$o.call(_optionsRef$current, {
formElement,
error: finalResult.error,
intent: intent$1
});
}
return finalResult;
}, [formRef, optionsRef]);
if (options.key !== keyRef.current) {
keyRef.current = options.key;
var formElement = dom.getFormElement(formRef);
if (formElement) {
future.dispatchInternalUpdateEvent(formElement);
}
setState(state.initializeState({
defaultValue: options.defaultValue
}));
} else if (lastResult && lastResult !== lastResultRef.current) {
lastResultRef.current = lastResult;
handleSubmission('server', lastResult, options);
}
react.useEffect(() => {
return () => {
var _abortControllerRef$c;
// Cancel pending validation request
(_abortControllerRef$c = abortControllerRef.current) === null || _abortControllerRef$c === void 0 || _abortControllerRef$c.abort('The component is unmounted');
};
}, []);
useSafeLayoutEffect(() => {
var formElement = dom.getFormElement(formRef);
// Reset the form values if the reset key changes
if (formElement && state$1.resetKey !== resetKeyRef.current) {
resetKeyRef.current = state$1.resetKey;
dom.resetFormValue(formElement, state$1.defaultValue, optionsRef.current.serialize);
pendingValueRef.current = undefined;
}
}, [formRef, state$1.resetKey, state$1.defaultValue, optionsRef]);
useSafeLayoutEffect(() => {
if (state$1.targetValue) {
var _formElement = dom.getFormElement(formRef);
if (!_formElement) {
// eslint-disable-next-line no-console
console.error('Failed to update form value; No form element found');
return;
}
dom.updateFormValue(_formElement, state$1.targetValue, optionsRef.current.serialize);
}
pendingValueRef.current = undefined;
}, [formRef, state$1.targetValue, optionsRef]);
var handleSubmit = react.useCallback(event => {
var _abortControllerRef$c2;
var abortController = new AbortController();
// Keep track of the abort controller so we can cancel the previous request if a new one is made
(_abortControllerRef$c2 = abortControllerRef.current) === null || _abortControllerRef$c2 === void 0 || _abortControllerRef$c2.abort('A new submission is made');
abortControllerRef.current = abortController;
var result;
var resolvedValue;
var formElement = event.currentTarget;
var submitEvent = dom.getSubmitEvent(event);
var formData = future.getFormData(formElement, submitEvent.submitter);
var submission = future.parseSubmission(formData, {
intentName: optionsRef.current.intentName
});
// Patch missing fields in the submission object
for (var element of formElement.elements) {
if (future.isFieldElement(element) && element.name) {
submission.fields = util.appendUniqueItem(submission.fields, element.name);
}
}
// Override submission value if the pending value is not applied yet (i.e. batch updates)
if (pendingValueRef.current !== undefined) {
submission.payload = pendingValueRef.current;
}
var lastAsyncResult = lastAsyncResultRef.current;
// Clear the last async result so it won't affect the next submission
lastAsyncResultRef.current = null;
if (lastAsyncResult &&
// Only default submission will be re-submitted after async validation
!submission.intent &&
// Ensure the submission payload is the same as the one being validated
future.deepEqual(submission.payload, lastAsyncResult.result.submission.payload)) {
result = lastAsyncResult.result;
resolvedValue = lastAsyncResult.resolvedValue;
} else {
var _optionsRef$current$o2, _optionsRef$current2;
var value = intent.resolveIntent(submission);
var submissionResult = future.report(submission, {
keepFiles: true,
value
});
var validateResult =
// Skip validation on form reset
value !== undefined ? (_optionsRef$current$o2 = (_optionsRef$current2 = optionsRef.current).onValidate) === null || _optionsRef$current$o2 === void 0 ? void 0 : _optionsRef$current$o2.call(_optionsRef$current2, {
payload: value,
error: {
formErrors: null,
fieldErrors: {}
},
intent: submission.intent ? intent.deserializeIntent(submission.intent) : null,
formElement,
submitter: submitEvent.submitter,
formData,
schemaValue: undefined
}) : {
error: null
};
var {
syncResult,
asyncResult
} = util.resolveValidateResult(validateResult);
if (typeof syncResult !== 'undefined') {
submissionResult.error = syncResult.error;
resolvedValue = syncResult.value;
}
if (typeof asyncResult !== 'undefined') {
// Update the form when the validation result is resolved
asyncResult.then(_ref => {
var {
error,
value
} = _ref;
// Update the form with the validation result
// There is no need to flush the update in this case
if (!abortController.signal.aborted) {
submissionResult.error = error;
handleSubmission('server', submissionResult);
// If the form is meant to be submitted and there is no error
if (submissionResult.error === null && !submission.intent) {
// Keep track of the validated payload and resume submission on the next task.
// Calling requestSubmit() directly from the async callback, or from a
// microtask, can still be ignored before the native submission lifecycle
// has fully settled.
setTimeout(() => {
if (abortController.signal.aborted) {
return;
}
lastAsyncResultRef.current = {
resolvedValue: value,
result: submissionResult
};
future.requestSubmit(formElement, submitEvent.submitter);
}, 0);
}
}
});
}
var clientResult = handleSubmission('client', submissionResult);
if (clientResult.reset || clientResult.targetValue !== undefined) {
var _ref2, _clientResult$targetV;
pendingValueRef.current = (_ref2 = (_clientResult$targetV = clientResult.targetValue) !== null && _clientResult$targetV !== void 0 ? _clientResult$targetV : optionsRef.current.defaultValue) !== null && _ref2 !== void 0 ? _ref2 : {};
}
if (
// If client validation happens
(typeof syncResult !== 'undefined' || typeof asyncResult !== 'undefined') && (
// Either the form is not meant to be submitted (i.e. intent is present) or there is an error / pending validation
clientResult.submission.intent || clientResult.error !== null)) {
event.preventDefault();
}
result = clientResult;
}
// We might not prevent form submission if server validation is required
// But the `onSubmit` handler should be triggered only if there is no intent
if (!event.isDefaultPrevented() && result.submission.intent === null) {
var _optionsRef$current$o3, _optionsRef$current3;
(_optionsRef$current$o3 = (_optionsRef$current3 = optionsRef.current).onSubmit) === null || _optionsRef$current$o3 === void 0 || _optionsRef$current$o3.call(_optionsRef$current3, event, {
formData,
get value() {
if (typeof resolvedValue === 'undefined') {
throw new Error('`value` is not available; Please make sure you have included the value in the `onValidate` result.');
}
return resolvedValue;
},
update(options) {
if (!abortController.signal.aborted) {
var _submissionResult = future.report(result.submission, _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, options), {}, {
keepFiles: true
}));
handleSubmission('server', _submissionResult);
}
}
});
}
}, [handleSubmission, optionsRef]);
return [state$1, handleSubmit];
}
/**
* A React hook that lets you sync the state of an input and dispatch native form events from it.
* This is useful when emulating native input behavior — typically by rendering a hidden base control
* and syncing it with a custom input.
*
* **Example:**
* ```ts
* const control = useControl(options);
* ```
*/
function useControl() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var observer = react.useContext(GlobalFormsObserverContext);
var inputRef = react.useRef(null);
var formRef = react.useMemo(() => ({
get current() {
var _input$0$form, _input$;
var input = inputRef.current;
if (!input) {
return null;
}
return Array.isArray(input) ? (_input$0$form = (_input$ = input[0]) === null || _input$ === void 0 ? void 0 : _input$.form) !== null && _input$0$form !== void 0 ? _input$0$form : null : input.form;
}
}), []);
var [defaultValue, setDefaultValue] = react.useState(() => dom.deriveDefaultPayload(options));
var pendingDefaultValueSyncRef = react.useRef(false);
/**
* Keep defaultValue in sync with external option updates during render.
* This is required for structural controls where hidden descendants must be
* rendered in the same cycle as form state updates (e.g. update intents).
*/
if (pendingDefaultValueSyncRef.current && inputRef.current && future.isGlobalInstance(inputRef.current, 'HTMLFieldSetElement')) {
pendingDefaultValueSyncRef.current = false;
setDefaultValue(() => dom.deriveDefaultPayload(options));
}
var eventDispatched = react.useRef({});
var snapshotRef = react.useRef(defaultValue);
var optionsRef = react.useRef(options);
react.useEffect(() => {
optionsRef.current = options;
});
react.useEffect(() => observer.onInternalUpdate(event => {
var input = inputRef.current;
if (input && input instanceof HTMLFieldSetElement && event.target === input.form) {
pendingDefaultValueSyncRef.current = true;
}
}), [observer]);
var payloadSnapshot = react.useSyncExternalStore(react.useCallback(callback => observer.onFieldUpdate(event => {
var _inputRef$current;
var input = event.target;
if (Array.isArray(inputRef.current) ? inputRef.current.some(item => item === input) : (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.contains(input)) {
callback();
}
}), [observer]), () => {
var input = inputRef.current;
var prev = snapshotRef.current;
var next = input ? dom.resolveControlPayload(input) : defaultValue;
if (future.deepEqual(prev, next)) {
return prev;
}
snapshotRef.current = next;
return next;
}, () => snapshotRef.current);
react.useEffect(() => {
var createEventListener = (listener, event) => {
var _inputRef$current2;
if (Array.isArray(inputRef.current) ? inputRef.current.some(item => item === event.target) : event.target instanceof Node && ((_inputRef$current2 = inputRef.current) === null || _inputRef$current2 === void 0 ? void 0 : _inputRef$current2.contains(event.target))) {
var timer = eventDispatched.current[listener];
if (timer) {
clearTimeout(timer);
}
eventDispatched.current[listener] = window.setTimeout(() => {
eventDispatched.current[listener] = undefined;
});
if (listener === 'focus') {
var _optionsRef$current4, _optionsRef$current4$;
(_optionsRef$current4 = optionsRef.current) === null || _optionsRef$current4 === void 0 || (_optionsRef$current4$ = _optionsRef$current4.onFocus) === null || _optionsRef$current4$ === void 0 || _optionsRef$current4$.call(_optionsRef$current4);
}
}
};
var inputHandler = createEventListener.bind(null, 'change');
var focusHandler = createEventListener.bind(null, 'focus');
var blurHandler = createEventListener.bind(null, 'blur');
document.addEventListener('input', inputHandler, true);
document.addEventListener('focusin', focusHandler, true);
document.addEventListener('focusout', blurHandler, true);
return () => {
document.removeEventListener('input', inputHandler, true);
document.removeEventListener('focusin', focusHandler, true);
document.removeEventListener('focusout', blurHandler, true);
};
}, []);
return {
defaultValue,
get payload() {
if (payloadSnapshot != null && 'parse' in options) {
try {
return options.parse(payloadSnapshot);
} catch (error) {
var payloadText = '';
try {
payloadText = JSON.stringify(payloadSnapshot, null, 2);
} catch (_unused) {
payloadText = '<unserializable payload>';
}
throw new Error("Failed to parse the payload. Received ".concat(payloadText, "."), {
cause: error
});
}
}
return payloadSnapshot;
},
get value() {
if (payloadSnapshot === null) {
return '';
}
if (typeof payloadSnapshot === 'string') {
return payloadSnapshot;
}
return undefined;
},
get checked() {
if (payloadSnapshot === null) {
return false;
}
var value = 'value' in options && options.value ? options.value : 'on';
if (payloadSnapshot === value) {
return true;
}
return undefined;
},
get options() {
if (payloadSnapshot === null) {
return [];
}
if (Array.isArray(payloadSnapshot) && payloadSnapshot.every(item => typeof item === 'string')) {
return payloadSnapshot;
}
return undefined;
},
get files() {
if (payloadSnapshot === null) {
return [];
}
if (Array.isArray(payloadSnapshot) && payloadSnapshot.every(item => future.isGlobalInstance(item, 'File'))) {
return payloadSnapshot;
}
return undefined;
},
formRef,
register: react.useCallback(element => {
if (!element) {
inputRef.current = null;
} else if (future.isFieldElement(element)) {
inputRef.current = element;
// Conform excludes hidden type inputs by default when updating form values
// Fix that by using the hidden attribute instead
if (element.type === 'hidden') {
element.hidden = true;
element.removeAttribute('type');
}
if (element.type === 'checkbox' || element.type === 'radio') {
// React set the value as empty string incorrectly when the value is undefined
// This make sure the checkbox value falls back to the default value "on" properly
// See https://github.com/facebook/react/issues/17590
var value = 'value' in optionsRef.current && optionsRef.current.value ? optionsRef.current.value : 'on';
element.value = value;
}
dom.initializeField(element, optionsRef.current);
} else if (element instanceof HTMLFieldSetElement) {
inputRef.current = element;
} else {
var _inputs$0$name, _inputs$, _inputs$0$type, _inputs$2;
var inputs = Array.from(element);
var name = (_inputs$0$name = (_inputs$ = inputs[0]) === null || _inputs$ === void 0 ? void 0 : _inputs$.name) !== null && _inputs$0$name !== void 0 ? _inputs$0$name : '';
var type = (_inputs$0$type = (_inputs$2 = inputs[0]) === null || _inputs$2 === void 0 ? void 0 : _inputs$2.type) !== null && _inputs$0$type !== void 0 ? _inputs$0$type : '';
if (!name || !(type === 'checkbox' || type === 'radio') || !inputs.every(input => input.name === name && input.type === type)) {
throw new Error('You can only register a checkbox or radio group with the same name');
}
inputRef.current = inputs;
if ('defaultValue' in optionsRef.current) {
for (var input of inputs) {
var _optionsRef$current5;
dom.initializeField(input, {
// We will not be uitlizing defaultChecked / value on checkbox / radio group
defaultValue: (_optionsRef$current5 = optionsRef.current) === null || _optionsRef$current5 === void 0 ? void 0 : _optionsRef$current5.defaultValue
});
}
}
}
}, []),
change: react.useCallback(value => {
if (!eventDispatched.current.change) {
var element = inputRef.current;
var isFieldset = element instanceof HTMLFieldSetElement;
var serializedValue = value == null ? value : 'serialize' in optionsRef.current && optionsRef.current.serialize ? optionsRef.current.serialize(value) : value;
if (isFieldset) {
// Fieldset mode renders hidden descendant inputs from defaultValue.
// Flush this update before dispatching events so listeners see the
// latest form structure in the same input/change cycle.
reactDom.flushSync(() => {
setDefaultValue(serializedValue);
});
}
if (element) {
future.change(element, serializedValue, {
// Sometimes no change is made on the inputs but done through DOM mutation.
// But we still want to dispatch the event to notify listeners.
forceDispatch: isFieldset
});
}
}
if (eventDispatched.current.change) {
clearTimeout(eventDispatched.current.change);
}
eventDispatched.current.change = undefined;
}, []),
focus: react.useCallback(() => {
if (!eventDispatched.current.focus) {
var element = Array.isArray(inputRef.current) ? inputRef.current[0] : inputRef.current;
if (element) {
future.focus(element);
}
}
if (eventDispatched.current.focus) {
clearTimeout(eventDispatched.current.focus);
}
eventDispatched.current.focus = undefined;
}, []),
blur: react.useCallback(() => {
if (!eventDispatched.current.blur) {
var element = Array.isArray(inputRef.current) ? inputRef.current[0] : inputRef.current;
if (element) {
future.blur(element);
}
}
if (eventDispatched.current.blur) {
clearTimeout(eventDispatched.current.blur);
}
eventDispatched.current.blur = undefined;
}, [])
};
}
/**
* A React hook that lets you subscribe to the current `FormData` of a form and derive a custom value from it.
* The selector runs whenever the form's structure or data changes, and the hook re-renders only when the result is deeply different.
*
* Returns `undefined` when the form element is not available (e.g., on SSR or initial client render),
* unless a `fallback` is provided.
*
* See https://conform.guide/api/react/future/useFormData
*
* **Example:**
* ```ts
* const value = useFormData(
* formRef,
* formData => formData.get('fieldName') ?? '',
* );
* ```
*/
function useFormData(formRef, select, options) {
var observer = react.useContext(GlobalFormsObserverContext);
var valueRef = react.useRef();
var formDataRef = react.useRef();
var value = react.useSyncExternalStore(react.useCallback(callback => {
var formElement = dom.getFormElement(formRef);
if (formElement) {
var formData = future.getFormData(formElement);
formDataRef.current = options !== null && options !== void 0 && options.acceptFiles ? formData : new URLSearchParams(Array.from(formData).map(_ref3 => {
var [key, value] = _ref3;
return [key, value.toString()];
}));
}
var unsubscribe = observer.onFormUpdate(event => {
if (event.target === dom.getFormElement(formRef)) {
var _formData = future.getFormData(event.target, event.submitter);
formDataRef.current = options !== null && options !== void 0 && options.acceptFiles ? _formData : new URLSearchParams(Array.from(_formData).map(_ref4 => {
var [key, value] = _ref4;
return [key, value.toString()];
}));
callback();
}
});
return unsubscribe;
}, [observer, formRef, options === null || options === void 0 ? void 0 : options.acceptFiles]), () => {
// Return fallback if form is not available
if (formDataRef.current === undefined) {
return options === null || options === void 0 ? void 0 : options.fallback;
}
var result = select(formDataRef.current, valueRef.current);
if (typeof valueRef.current !== 'undefined' && future.deepEqual(result, valueRef.current)) {
return valueRef.current;
}
valueRef.current = result;
return result;
}, () => options === null || options === void 0 ? void 0 : options.fallback);
return value;
}
/**
* useLayoutEffect is client-only.
* This basically makes it a no-op on server
*/
var useSafeLayoutEffect = typeof document === 'undefined' ? react.useEffect : react.useLayoutEffect;
/**
* Keep a mutable ref in sync with the latest value.
* Useful to avoid stale closures in event handlers or async callbacks.
*/
function useLatest(value) {
var ref = react.useRef(value);
useSafeLayoutEffect(() => {
ref.current = value;
}, [value]);
return ref;
}
/**
* A component that renders hidden base control(s) based on the shape of defaultValue.
* Used with useControl to sync complex values with form data.
*
* **Example:**
* ```tsx
* const control = useControl<{ street: string; city: string }>({
* defaultValue: { street: '123 Main St', city: 'Anytown' },
* parse(payload) {
* if (
* typeof payload === 'object' &&
* payload !== null &&
* 'street' in payload &&
* 'city' in payload &&
* typeof payload.street === 'string' &&
* typeof payload.city === 'string'
* ) {
* return payload;
* }
*
* throw new Error('Unexpected payload shape');
* },
* });
*
* <BaseControl
* type="fieldset"
* name="address"
* ref={control.register}
* defaultValue={control.defaultValue}
* />
* ```
*/
var BaseControl = /*#__PURE__*/react.forwardRef(function BaseControl(props, ref) {
function formatValue(value) {
var serialized = future.defaultSerialize(value);
if (typeof serialized === 'string') {
return serialized;
}
// null, undefined, File, or array - fallback to empty string
return '';
}
function renderInput(name, value, form) {
if (Array.isArray(value)) {
return value.map((item, index) => renderInput("".concat(name, "[").concat(index, "]"), item, form));
}
if (future.isPlainObject(value)) {
return Object.entries(value).map(_ref5 => {
var [key, val] = _ref5;
return renderInput("".concat(name, ".").concat(key), val, form);
});
}
return /*#__PURE__*/jsxRuntime.jsx("input", {
name: name,
defaultValue: formatValue(value),
form: form
}, name);
}
if (props.type === 'fieldset') {
var {
name,
form,
defaultValue: _defaultValue,
hidden: _hidden = true
} = props,
fieldsetProps = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded);
return /*#__PURE__*/jsxRuntime.jsx("fieldset", _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, fieldsetProps), {}, {
ref: ref,
name: name,
form: form,
hidden: _hidden,
children: _defaultValue != null ? renderInput(name, _defaultValue, form) : null
}));
}
if (props.type === 'select') {
var {
defaultValue: _defaultValue2,
multiple = Array.isArray(_defaultValue2),
hidden: _hidden2 = true
} = props,
selectProps = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded2);
if (multiple) {
var defaultOptions = Array.isArray(_defaultValue2) ? _defaultValue2.map(formatValue) : [formatValue(_defaultValue2)];
return /*#__PURE__*/jsxRuntime.jsx("select", _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, selectProps), {}, {
ref: ref,
defaultValue: defaultOptions,
hidden: _hidden2,
multiple: true,
children: defaultOptions.map((option, index) => /*#__PURE__*/jsxRuntime.jsx("option", {
value: option,
children: option
}, index))
}));
}
var defaultOption = formatValue(_defaultValue2);
return /*#__PURE__*/jsxRuntime.jsx("select", _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, selectProps), {}, {
ref: ref,
defaultValue: defaultOption,
hidden: _hidden2,
children: [defaultOption].map((option, index) => /*#__PURE__*/jsxRuntime.jsx("option", {
value: option,
children: option
}, index))
}));
}
if (props.type === 'textarea') {
var {
defaultValue: _defaultValue3,
hidden: _hidden3 = true
} = props,
textareaProps = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded3);
return /*#__PURE__*/jsxRuntime.jsx("textarea", _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, textareaProps), {}, {
defaultValue: formatValue(_defaultValue3),
ref: ref,
hidden: _hidden3
}));
}
if (props.type === 'checkbox' || props.type === 'radio') {
var {
defaultValue: _defaultValue4 = 'on',
value = _defaultValue4,
hidden: _hidden4 = true
} = props,
_inputProps = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded4);
return /*#__PURE__*/jsxRuntime.jsx("input", _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, _inputProps), {}, {
ref: ref,
value: value,
hidden: _hidden4
}));
}
var {
defaultValue,
hidden = true
} = props,
inputProps = _rollupPluginBabelHelpers.objectWithoutProperties(props, _excluded5);
return /*#__PURE__*/jsxRuntime.jsx("input", _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, inputProps), {}, {
ref: ref,
defaultValue: defaultValue !== undefined ? formatValue(defaultValue) : undefined,
hidden: hidden
}));
});
exports.BaseControl = BaseControl;
exports.GlobalFormsObserverContext = GlobalFormsObserverContext;
exports.INITIAL_KEY = INITIAL_KEY;
exports.PreserveBoundary = PreserveBoundary;
exports.useConform = useConform;
exports.useControl = useControl;
exports.useFormData = useFormData;
exports.useLatest = useLatest;
exports.useSafeLayoutEffect = useSafeLayoutEffect;