@conform-to/react
Version:
Conform view adapter for react
227 lines (213 loc) • 6.99 kB
JavaScript
Object.defineProperty(exports, '__esModule', { value: true });
var future = require('@conform-to/dom/future');
var intent = require('./intent.js');
function getFormElement(formRef) {
var _element$form;
if (typeof formRef === 'string') {
return document.forms.namedItem(formRef);
}
var element = formRef === null || formRef === void 0 ? void 0 : formRef.current;
if (element instanceof HTMLFormElement) {
return element;
}
return (_element$form = element === null || element === void 0 ? void 0 : element.form) !== null && _element$form !== void 0 ? _element$form : null;
}
function getSubmitEvent(event) {
if (event.type !== 'submit') {
throw new Error('The event is not a submit event');
}
return event.nativeEvent;
}
function initializeField(element, options) {
var _options$value;
if (element.dataset.conform) {
return;
}
var defaultValue = typeof (options === null || options === void 0 ? void 0 : options.value) === 'string' || typeof (options === null || options === void 0 ? void 0 : options.defaultChecked) === 'boolean' ? options.defaultChecked ? (_options$value = options.value) !== null && _options$value !== void 0 ? _options$value : 'on' : null : options === null || options === void 0 ? void 0 : options.defaultValue;
// Update the value of the element, including the default value
future.updateField(element, {
value: defaultValue,
defaultValue
});
element.dataset.conform = 'initialized';
}
/**
* Makes hidden form inputs focusable with visually hidden styles
*/
function makeInputFocusable(element) {
if (!element.hidden && element.type !== 'hidden') {
return;
}
// Style the element to be visually hidden
element.style.position = 'absolute';
element.style.width = '1px';
element.style.height = '1px';
element.style.padding = '0';
element.style.margin = '-1px';
element.style.overflow = 'hidden';
element.style.clip = 'rect(0,0,0,0)';
element.style.whiteSpace = 'nowrap';
element.style.border = '0';
// Hide the element from screen readers
element.setAttribute('aria-hidden', 'true');
// Make sure people won't tab to this element
element.tabIndex = -1;
// Set the element to be visible again so it can be focused
if (element.hidden) {
element.hidden = false;
}
if (element.type === 'hidden') {
element.setAttribute('type', 'text');
}
}
function getRadioGroupValue(inputs) {
for (var input of inputs) {
if (input.type === 'radio' && input.checked) {
return input.value;
}
}
}
function getCheckboxGroupValue(inputs) {
var values;
for (var input of inputs) {
if (input.type === 'checkbox') {
var _values;
(_values = values) !== null && _values !== void 0 ? _values : values = [];
if (input.checked) {
values.push(input.value);
}
}
}
return values;
}
function getInputSnapshot(input) {
if (input instanceof HTMLInputElement) {
switch (input.type) {
case 'file':
return {
files: input.files ? Array.from(input.files) : undefined
};
case 'radio':
case 'checkbox':
return {
value: input.value,
checked: input.checked
};
}
} else if (input instanceof HTMLSelectElement && input.multiple) {
return {
options: Array.from(input.selectedOptions).map(option => option.value)
};
}
return {
value: input.value
};
}
/**
* Creates an InputSnapshot based on the provided options:
* - checkbox/radio: value / defaultChecked
* - file inputs: defaultValue is File or FileList
* - select multiple: defaultValue is string array
* - others: defaultValue is string
*/
function createDefaultSnapshot(defaultValue, defaultChecked, value) {
if (typeof value === 'string' || typeof defaultChecked === 'boolean') {
return {
value: value !== null && value !== void 0 ? value : 'on',
checked: defaultChecked
};
}
if (typeof defaultValue === 'string') {
return {
value: defaultValue
};
}
if (Array.isArray(defaultValue)) {
if (defaultValue.every(item => typeof item === 'string')) {
return {
options: defaultValue
};
} else {
return {
files: defaultValue
};
}
}
if (future.isGlobalInstance(defaultValue, 'File')) {
return {
files: [defaultValue]
};
}
if (future.isGlobalInstance(defaultValue, 'FileList')) {
return {
files: Array.from(defaultValue)
};
}
return {};
}
/**
* Focuses the first field with validation errors on default form submission.
* Does nothing if the submission was triggered with a specific intent (e.g. validate / insert)
*/
function focusFirstInvalidField(ctx) {
if (ctx.intent) {
return;
}
for (var element of ctx.formElement.elements) {
var _ctx$error$fieldError;
if (future.isFieldElement(element) && (_ctx$error$fieldError = ctx.error.fieldErrors[element.name]) !== null && _ctx$error$fieldError !== void 0 && _ctx$error$fieldError.length) {
element.focus();
break;
}
}
}
function updateFormValue(form, intendedValue, serialize) {
for (var element of form.elements) {
if (future.isFieldElement(element) && element.name) {
var value = future.getValueAtPath(intendedValue, element.name);
var serializedValue = serialize(value);
if (typeof serializedValue !== 'undefined') {
future.change(element, serializedValue, {
preventDefault: true
});
}
}
}
}
/**
* Creates a proxy that dynamically generates intent dispatch functions.
* Each property access returns a function that submits the intent to the form.
*/
function createIntentDispatcher(formElement, intentName) {
return new Proxy({}, {
get(target, type, receiver) {
if (typeof type === 'string') {
var _target$type;
// @ts-expect-error
(_target$type = target[type]) !== null && _target$type !== void 0 ? _target$type : target[type] = payload => {
var form = typeof formElement === 'function' ? formElement() : formElement;
if (!form) {
throw new Error("Dispatching \"".concat(type, "\" intent failed; No form element found."));
}
future.requestIntent(form, intentName, intent.serializeIntent({
type,
payload
}));
};
}
return Reflect.get(target, type, receiver);
}
});
}
exports.createDefaultSnapshot = createDefaultSnapshot;
exports.createIntentDispatcher = createIntentDispatcher;
exports.focusFirstInvalidField = focusFirstInvalidField;
exports.getCheckboxGroupValue = getCheckboxGroupValue;
exports.getFormElement = getFormElement;
exports.getInputSnapshot = getInputSnapshot;
exports.getRadioGroupValue = getRadioGroupValue;
exports.getSubmitEvent = getSubmitEvent;
exports.initializeField = initializeField;
exports.makeInputFocusable = makeInputFocusable;
exports.updateFormValue = updateFormValue;
;