UNPKG

@steveesamson/microform

Version:

`microform` is a tiny library for managing forms in `svelte/sveltekit`.

107 lines (106 loc) 4.25 kB
import { onMount } from 'svelte'; import { useValidator } from './form-validators.js'; import { debounce, getEditableContent } from './utils.js'; const isField = (node) => { return (node instanceof HTMLSelectElement || node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement); }; const isExcluded = (node) => { return (node instanceof HTMLInputElement && ['radio', 'checkbox'].includes(node.type.toLowerCase())); }; const isCheckbox = (node) => { return node instanceof HTMLInputElement && ['checkbox'].includes(node.type.toLowerCase()); }; const isRadio = (node) => { return node instanceof HTMLInputElement && ['radio'].includes(node.type.toLowerCase()); }; const checkFormFitness = (values, validationMap, validate) => { for (const [name, { validations }] of Object.entries(validationMap)) { validate({ name, value: values[name], validations }); } }; export const formAction = (values, errors, unfits, onSanity, options, validationMap) => { const { validators: customValidators } = options; const { validate, validators } = useValidator(errors, values); // override if (customValidators) { for (const [key, val] of Object.entries(customValidators)) { validators[key] = val; } } const hasError = (next) => !!next; let canWatch = false; // $state(false); const evaluateSanity = () => { const { validate: validateUnfit } = useValidator(unfits, values, validators); checkFormFitness(values, validationMap, validateUnfit); const withErrors = Object.values(errors).some(hasError); const withUnfits = Object.values(unfits).some(hasError); onSanity(!withErrors && !withUnfits && canWatch); }; $effect(() => { if (options.debug) { console.log('values changed...'); } Object.values(values); evaluateSanity(); }); const start = debounce(() => { canWatch = true; if (options.debug) { console.log('microform initialized...'); } }, options.fieldWaitTimeInMilliSecond); return (node, eventProps) => { start(); const nodeName = isField(node) ? node.name : ''; const { name: dsname = nodeName } = node.dataset || {}; const { name = dsname, validations = [], validateEvent = options.validateEvent, html = false } = eventProps || {}; validationMap[name] = { validations, html, nodeRef: node }; const storedValue = values[name] || ''; let defValue = storedValue; if (isField(node) && !isExcluded(node)) { defValue = node.value || storedValue; node.value = defValue; } else if (node.isContentEditable) { defValue = node.innerHTML || storedValue; node.innerHTML = defValue; } values[name] = defValue; const updateNode = (e) => { if (isField(node) && !isExcluded(node)) { const value = e.target.value || ''; values[name] = value; } else if (node.isContentEditable) { const { value: htm, text } = getEditableContent({ target: node }, html); values[name] = htm; values[`${name}-text`] = text; } else if (isCheckbox(node)) { const { checked, value: val } = node; const fieldValue = String(values[name] ?? ''); let current = fieldValue?.split(','); if (checked) { current.push(val); } else { current = current.filter((next) => next !== val); } values[name] = [...new Set(current)].join(','); } else if (isRadio(node)) { const { value: fvalue } = node; values[name] = fvalue; } validate({ name, value: values[name], validations, node }); }; onMount(() => { node.addEventListener(validateEvent, updateNode); return () => { node.removeEventListener(validateEvent, updateNode); }; }); }; };