diff --git a/lib/components/Field.svelte b/lib/components/Field.svelte index dd99864..2289de5 100644 --- a/lib/components/Field.svelte +++ b/lib/components/Field.svelte @@ -12,6 +12,7 @@ {name} type={type} value={$form[name]} + checked={$form[name]} on:change={handleChange} on:blur={handleChange} {...$$props} /> diff --git a/lib/components/Submit.svelte b/lib/components/Submit.svelte new file mode 100644 index 0000000..d8feab4 --- /dev/null +++ b/lib/components/Submit.svelte @@ -0,0 +1,11 @@ + + + diff --git a/lib/createForm.js b/lib/createForm.js index 0b49406..7a64cb2 100644 --- a/lib/createForm.js +++ b/lib/createForm.js @@ -5,168 +5,174 @@ const NO_ERROR = ""; const IS_TOUCHED = true; export const createForm = config => { - const initialValues = config.initialValues || {}; - - if (Object.keys(initialValues).length < 1) { - const provided = JSON.stringify(initialValues); - console.warn( - `createForm requires initialValues to be a non empty object or array, provided ${provided}` - ); - return; - } - - const validationSchema = config.validationSchema; - const validateFn = config.validate; - const onSubmit = config.onSubmit; - - const initial = { - values: () => util.cloneDeep(initialValues), - errors: () => util.assignDeep(initialValues, NO_ERROR), - touched: () => util.assignDeep(initialValues, !IS_TOUCHED) - }; - - const form = writable(initial.values()); - const errors = writable(initial.errors()); - const touched = writable(initial.touched()); - - const isSubmitting = writable(false); - const isValidating = writable(false); - - const isValid = derived([errors, touched], ([$errors, $touched]) => { - const allTouched = util - .getValues($touched) - .every(field => field === IS_TOUCHED); - const noErrors = util.getValues($errors).every(field => field === NO_ERROR); - return allTouched && noErrors; - }); - - function isCheckbox(el) { - return el.getAttribute && el.getAttribute('type') === 'checkbox'; - } - - function handleChange(event) { - const el = event.target; - const field = el.name; - const value = isCheckbox(el) ? el.checked : el.value; - - updateTouched(field, true); - - if (validationSchema) { - isValidating.set(true); - return util - .reach(validationSchema, field) - .validate(value) - .then(() => util.update(errors, field, "")) - .catch(err => util.update(errors, field, err.message)) - .finally(() => { - updateField(field, value); - isValidating.set(false); - }); + const initialValues = config.initialValues || {}; + + if (Object.keys(initialValues).length < 1) { + const provided = JSON.stringify(initialValues); + console.warn( + `createForm requires initialValues to be a non empty object or array, provided ${provided}` + ); + return; } - if (validateFn) { - isValidating.set(true); - return Promise.resolve() - .then(() => validateFn({ [field]: value })) - .then(errs => util.update(errors, field, errs[field])) - .finally(() => { - updateField(field, value); - isValidating.set(false); - }) - } + const validationSchema = config.validationSchema; + const validateFn = config.validate; + const onSubmit = config.onSubmit; + + const initial = { + values: () => util.cloneDeep(initialValues), + errors: () => util.assignDeep(initialValues, NO_ERROR), + touched: () => util.assignDeep(initialValues, !IS_TOUCHED) + }; + + const form = writable(initial.values()); + const errors = writable(initial.errors()); + const touched = writable(initial.touched()); + + let context; + form.subscribe(val => context = val); + + const isSubmitting = writable(false); + const isValidating = writable(false); + + const isValid = derived([errors, touched], ([$errors, $touched]) => { + /* + const allTouched = util + .getValues($touched) + .every(field => field === IS_TOUCHED); + */ + const noErrors = util.getValues($errors).every(field => field === NO_ERROR); + return /* allTouched && */ noErrors; + }); - updateField(field, value); - } + function isCheckbox(el) { + return el.getAttribute && el.getAttribute('type') === 'checkbox'; + } - function handleSubmit(ev) { - if (ev && ev.preventDefault) { - ev.preventDefault(); + function handleChange(event) { + const el = event.target; + const field = el.name; + const value = isCheckbox(el) ? el.checked : el.value; + + updateTouched(field, true); + + if (validationSchema) { + isValidating.set(true); + return validationSchema + .validateAt(field, context) + .then(() => util.update(errors, field, "")) + .catch(err => util.update(errors, field, err.message)) + .finally(() => { + updateField(field, value); + isValidating.set(false); + }); + } + + if (validateFn) { + isValidating.set(true); + return Promise.resolve() + .then(() => validateFn({ + [field]: value + })) + .then(errs => util.update(errors, field, errs[field])) + .finally(() => { + updateField(field, value); + isValidating.set(false); + }) + } + + updateField(field, value); } - isSubmitting.set(true); + function handleSubmit(ev) { + if (ev && ev.preventDefault) { + ev.preventDefault(); + } - return util.subscribeOnce(form).then(values => { - if (typeof validateFn === "function") { - isValidating.set(true); + isSubmitting.set(true); - return Promise.resolve() - .then(() => validateFn(values)) - .then(err => - util.isEmpty(err) ? clearErrorsAndSubmit(values) : errors.set(err) - ) - .finally(() => isValidating.set(false)); - } - - if (validationSchema) { - isValidating.set(true); - - return validationSchema - .validate(values, { abortEarly: false }) - .then(() => clearErrorsAndSubmit(values)) - .catch(yupErrs => { - if (yupErrs && yupErrs.inner) { - yupErrs.inner.forEach(error => - util.update(errors, error.path, error.message) - ); + return util.subscribeOnce(form).then(values => { + if (typeof validateFn === "function") { + isValidating.set(true); + + return Promise.resolve() + .then(() => validateFn(values)) + .then(err => + util.isEmpty(err) ? clearErrorsAndSubmit(values) : errors.set(err) + ) + .finally(() => isValidating.set(false)); } - isSubmitting.set(false); - }) - .finally(() => isValidating.set(false)); - } - clearErrorsAndSubmit(values); - }); - } - - function handleReset() { - form.set(initial.values()); - errors.set(initial.errors()); - touched.set(initial.touched()); - } - - function clearErrorsAndSubmit(values) { - return Promise.resolve() - .then(() => errors.set(util.assignDeep(values, ""))) - .then(() => onSubmit(values, form, errors)) - .finally(() => isSubmitting.set(false)); - } - - /** - * Handler to imperatively update the value of a form field - */ - function updateField(field, value) { - util.update(form, field, value); - } - - /** - * Handler to imperatively update the touched value of a form field - */ - function updateTouched(field, value) { - util.update(touched, field, value); - } - - return { - form, - errors, - touched, - isValid, - isSubmitting, - isValidating, - handleChange, - handleSubmit, - handleReset, - updateField, - updateTouched, - state: derived( - [form, errors, touched, isValid, isValidating, isSubmitting], - ([$form, $errors, $touched, $isValid, $isValidating, $isSubmitting]) => ({ - form: $form, - errors: $errors, - touched: $touched, - isValid: $isValid, - isSubmitting: $isSubmitting, - isValidating: $isValidating - }) - ) - }; -}; + if (validationSchema) { + isValidating.set(true); + + return validationSchema + .validate(values, { abortEarly: false }) + .then(() => clearErrorsAndSubmit(values)) + .catch(yupErrs => { + if (yupErrs && yupErrs.inner) { + yupErrs.inner.forEach(error => + util.update(errors, error.path, error.message) + ); + } + isSubmitting.set(false); + }) + .finally(() => isValidating.set(false)); + } + + clearErrorsAndSubmit(values); + }); + } + + function handleReset() { + form.set(initial.values()); + errors.set(initial.errors()); + touched.set(initial.touched()); + } + + function clearErrorsAndSubmit(values) { + return Promise.resolve() + .then(() => errors.set(util.assignDeep(values, ""))) + .then(() => onSubmit(values, form, errors)) + .finally(() => isSubmitting.set(false)); + } + + /** + * Handler to imperatively update the value of a form field + */ + function updateField(field, value) { + util.update(form, field, value); + } + + /** + * Handler to imperatively update the touched value of a form field + */ + function updateTouched(field, value) { + util.update(touched, field, value); + } + + return { + form, + errors, + touched, + isValid, + isSubmitting, + isValidating, + handleChange, + handleSubmit, + handleReset, + updateField, + updateTouched, + state: derived( + [form, errors, touched, isValid, isValidating, isSubmitting], + ([$form, $errors, $touched, $isValid, $isValidating, $isSubmitting]) => ({ + form: $form, + errors: $errors, + touched: $touched, + isValid: $isValid, + isSubmitting: $isSubmitting, + isValidating: $isValidating + }) + ) + }; +}; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index 57d9b7a..fcc3e36 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,5 +1,12 @@ -export { createForm } from "./createForm"; -export { default as Form } from "./components/Form.svelte"; -export { default as Field } from "./components/Field.svelte"; -export { default as Select } from "./components/Select.svelte"; -export { default as ErrorMessage } from "./components/ErrorMessage.svelte"; +export { createForm } +from "./createForm"; +export { default as Form } +from "./components/Form.svelte"; +export { default as Field } +from "./components/Field.svelte"; +export { default as Select } +from "./components/Select.svelte"; +export { default as ErrorMessage } +from "./components/ErrorMessage.svelte"; +export { default as Submit } +from "./components/Submit.svelte"; \ No newline at end of file