UNPKG

@conform-to/react

Version:

Conform view adapter for react

801 lines (774 loc) 32.2 kB
'use client'; 'use strict'; 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;