UNPKG

@muban/muban

Version:

Writing components for server-rendered HTML

87 lines (86 loc) 4.86 kB
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); }, }; }; }