@conform-to/react
Version:
Conform view adapter for react
280 lines (273 loc) • 9.68 kB
JavaScript
;
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;