@muban/muban
Version:
Writing components for server-rendered HTML
87 lines (86 loc) • 4.86 kB
JavaScript
import dedent from 'ts-dedent';
import { convertSourceValue } from './convertSourceValue';
export function createFormPropertySource() {
return () => {
return {
sourceName: 'form',
hasProp: (propInfo) => Boolean(propInfo.source.target && propInfo.type !== Function),
getProp: (propInfo) => {
const element = propInfo.source.target;
const isCheckbox = element.nodeName === 'INPUT' && element.type === 'checkbox';
const isRadio = element.nodeName === 'INPUT' && element.type === 'radio';
const isForm = element.nodeName === 'FORM';
const isInput = ['INPUT', 'TEXTAREA', 'SELECT'].includes(element.nodeName) &&
element.type !== 'checkbox';
const isMultiSelect = element.nodeName === 'SELECT' && element.multiple;
const isValidtag = ['INPUT', 'FORM', 'TEXTAREA', 'SELECT'].includes(element.nodeName);
if (!isValidtag) {
// eslint-disable-next-line no-console
console.warn(dedent `The property "${propInfo.name}" of type "${propInfo.type.name}" requires an element of type input, form, textarea, or select. ${element.nodeName} was given
Returning "undefined".`);
return undefined;
}
const formDataValue = (previousValue) => {
if (!isForm)
return previousValue;
const formData = new FormData(element);
if (propInfo.source.formData) {
if (propInfo.type !== Object) {
// eslint-disable-next-line no-console
console.warn(dedent `The property "${propInfo.name}" is trying to get a FormData object but is type "${propInfo.type.name}" use type "Object" if you want the full FormData object
Returning "undefined".`);
return undefined;
}
return formData;
}
const childInputValues = formData.getAll(propInfo.source.name || '');
if (childInputValues.length > 0 && propInfo.type === Array) {
let valueIsStringifiedArray = false;
try {
const parsedValue = JSON.parse(childInputValues[0]);
if (Array.isArray(parsedValue))
valueIsStringifiedArray = true;
// eslint-disable-next-line no-empty
}
catch (_a) { }
if (!valueIsStringifiedArray)
return childInputValues;
}
if (childInputValues.length === 0)
return undefined;
return convertSourceValue(propInfo, childInputValues[0] || '');
};
const textInput = (previousValue) => {
const input = element;
if (isRadio) {
// eslint-disable-next-line no-console
console.warn(dedent `The property "${propInfo.name}" is trying to get a radio button value but the target is not the parent form, if you have multiple radio buttons with a shared name use the parent form as target
Returning the input value "${input.value}" despite the fact it could be unchecked.`);
}
if (isInput && !input.multiple)
return convertSourceValue(propInfo, input.value);
return previousValue;
};
const checkbox = (previousValue) => {
const input = element;
if (isCheckbox && propInfo.type === Boolean)
return input.checked;
return previousValue;
};
const nonBooleanCheckbox = (previousValue) => {
const input = element;
if (isCheckbox && propInfo.type !== Boolean && input.checked)
return convertSourceValue(propInfo, input.value);
return previousValue;
};
const multiSelect = (previousValue) => {
if (isMultiSelect) {
return Array.from(element.selectedOptions).map((option) => option.value);
}
return previousValue;
};
return [formDataValue, textInput, checkbox, nonBooleanCheckbox, multiSelect].reduce((previous, current) => current(previous), undefined);
},
};
};
}