UNPKG

@conform-to/react

Version:

Conform view adapter for react

280 lines (273 loc) 9.68 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _rollupPluginBabelHelpers = require('./_virtual/_rollupPluginBabelHelpers.js'); var dom = require('@conform-to/dom'); var react = require('react'); var jsxRuntime = require('react/jsx-runtime'); var Form = /*#__PURE__*/react.createContext([]); // To hide the FormContext type from the public API var wrappedSymbol = Symbol('wrapped'); function getWrappedFormContext(context) { return context[wrappedSymbol]; } function useFormContext(formId) { var contexts = react.useContext(Form); var form = formId ? contexts.find(context => formId === context.getFormId()) : contexts[0]; if (!form) { throw new Error('Form context is not available'); } return form; } function useFormState(form, subjectRef) { var subscribe = react.useCallback(callback => form.subscribe(callback, () => subjectRef === null || subjectRef === void 0 ? void 0 : subjectRef.current), [form, subjectRef]); return react.useSyncExternalStore(subscribe, form.getState, form.getState); } function FormProvider(props) { var forms = react.useContext(Form); var context = getWrappedFormContext(props.context); var value = react.useMemo(() => [context].concat(forms), // Put the latest form context first [forms, context]); return /*#__PURE__*/jsxRuntime.jsx(Form.Provider, { value: value, children: props.children }); } function FormStateInput(props) { var context = useFormContext(props.formId); return /*#__PURE__*/jsxRuntime.jsx("input", { type: "hidden", name: dom.STATE, value: context.getSerializedState(), form: props.formId }); } function useSubjectRef() { var initialSubject = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var subjectRef = react.useRef(initialSubject); // Reset the subject everytime the component is rerendered // This let us subscribe to data used in the last render only subjectRef.current = initialSubject; return subjectRef; } function updateSubjectRef(ref, subject, scope, name) { if (subject === 'status' || subject === 'formId' || subject === 'pendingIntents') { ref.current[subject] = true; } else if (typeof scope !== 'undefined' && typeof name !== 'undefined') { var _ref$current$subject$, _ref$current$subject; ref.current[subject] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, ref.current[subject]), {}, { [scope]: ((_ref$current$subject$ = (_ref$current$subject = ref.current[subject]) === null || _ref$current$subject === void 0 ? void 0 : _ref$current$subject[scope]) !== null && _ref$current$subject$ !== void 0 ? _ref$current$subject$ : []).concat(name) }); } } function getMetadata(context, subjectRef, stateSnapshot) { var name = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ''; var id = name ? "".concat(context.getFormId(), "-").concat(name) : context.getFormId(); var state = context.getState(); return new Proxy({ id, name, errorId: "".concat(id, "-error"), descriptionId: "".concat(id, "-description"), get defaultValue() { var initialValue = this.initialValue; if (typeof initialValue === 'string') { return initialValue; } if (Array.isArray(initialValue)) { return initialValue[0]; } }, get defaultOptions() { var initialValue = this.initialValue; if (typeof initialValue === 'string') { return [initialValue]; } if (Array.isArray(initialValue) && initialValue.every(item => typeof item === 'string')) { return initialValue; } }, get defaultChecked() { if (this.initialValue === 'on') { return true; } }, get initialValue() { return state.initialValue[name]; }, get value() { return state.value[name]; }, get errors() { return state.error[name]; }, get key() { return state.key[name]; }, get valid() { return state.valid[name]; }, get dirty() { return state.dirty[name]; }, get allErrors() { if (name === '') { return state.error; } var result = {}; for (var [key, error] of Object.entries(state.error)) { if (dom.isPrefix(key, name)) { result[key] = error; } } return result; }, get getFieldset() { return () => new Proxy({}, { get(target, key, receiver) { if (typeof key === 'string') { return getFieldMetadata(context, subjectRef, stateSnapshot, name, key); } return Reflect.get(target, key, receiver); } }); } }, { get(target, key, receiver) { // We want to minize re-render by identifying whether the field is used in a callback only // but there is no clear way to know if it is accessed during render or not // if the stateSnapshot is not the latest, then it must be accessed in a callback if (state === stateSnapshot) { switch (key) { case 'id': case 'errorId': case 'descriptionId': updateSubjectRef(subjectRef, 'formId'); break; case 'key': case 'initialValue': case 'value': case 'valid': case 'dirty': updateSubjectRef(subjectRef, key, 'name', name); break; case 'errors': case 'allErrors': updateSubjectRef(subjectRef, 'error', key === 'errors' ? 'name' : 'prefix', name); break; } } return Reflect.get(target, key, receiver); } }); } function getFieldMetadata(context, subjectRef, stateSnapshot) { var prefix = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ''; var key = arguments.length > 4 ? arguments[4] : undefined; var name = typeof key === 'undefined' ? prefix : dom.formatPaths([...dom.getPaths(prefix), key]); return new Proxy({}, { get(_, key, receiver) { var _state$constraint$nam; var metadata = getMetadata(context, subjectRef, stateSnapshot, name); var state = context.getState(); switch (key) { case 'formId': if (state === stateSnapshot) { updateSubjectRef(subjectRef, 'formId'); } return context.getFormId(); case 'required': case 'minLength': case 'maxLength': case 'min': case 'max': case 'pattern': case 'step': case 'multiple': return (_state$constraint$nam = state.constraint[name]) === null || _state$constraint$nam === void 0 ? void 0 : _state$constraint$nam[key]; case 'getFieldList': { return () => { var _state$initialValue$n; var initialValue = (_state$initialValue$n = state.initialValue[name]) !== null && _state$initialValue$n !== void 0 ? _state$initialValue$n : []; if (state === stateSnapshot) { updateSubjectRef(subjectRef, 'initialValue', 'name', name); } if (!Array.isArray(initialValue)) { throw new Error('The initial value at the given name is not a list'); } return Array(initialValue.length).fill(0).map((_, index) => getFieldMetadata(context, subjectRef, stateSnapshot, name, index)); }; } } return Reflect.get(metadata, key, receiver); } }); } function getFormMetadata(context, subjectRef, stateSnapshot, noValidate) { return new Proxy({}, { get(_, key, receiver) { var metadata = getMetadata(context, subjectRef, stateSnapshot); var state = context.getState(); switch (key) { case 'context': return { [wrappedSymbol]: context }; case 'status': if (state === stateSnapshot) { updateSubjectRef(subjectRef, 'status'); } return state.submissionStatus; case 'validate': case 'update': case 'reset': case 'insert': case 'remove': case 'reorder': return context[key]; case 'onSubmit': return context.submit; case 'noValidate': return noValidate; } return Reflect.get(metadata, key, receiver); } }); } function createFormContext(options) { var { onSubmit } = options; var context = dom.unstable_createFormContext(options); return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, context), {}, { submit(event) { var submitEvent = event.nativeEvent; var result = context.submit(submitEvent); if (!result.submission || result.submission.status === 'success' || result.submission.error === null) { if (!result.formData.has(dom.INTENT)) { var _onSubmit; (_onSubmit = onSubmit) === null || _onSubmit === void 0 || _onSubmit(event, result); } } else { event.preventDefault(); } }, onUpdate(options) { onSubmit = options.onSubmit; context.onUpdate(options); } }); } exports.Form = Form; exports.FormProvider = FormProvider; exports.FormStateInput = FormStateInput; exports.createFormContext = createFormContext; exports.getFieldMetadata = getFieldMetadata; exports.getFormMetadata = getFormMetadata; exports.getMetadata = getMetadata; exports.getWrappedFormContext = getWrappedFormContext; exports.updateSubjectRef = updateSubjectRef; exports.useFormContext = useFormContext; exports.useFormState = useFormState; exports.useSubjectRef = useSubjectRef;