Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

svelte-formula

Package Overview
Dependencies
Maintainers
1
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

svelte-formula - npm Package Compare versions

Comparing version 0.1.1 to 0.2.0

types/validation.d.ts

67

CHANGELOG.md

@@ -8,2 +8,66 @@ # Changelog

## [0.2.0] 2021-02-15
### Changed
- `formValid` now `isFormValid`
### Added
- Support for custom field-level validators via the `validators` property of the `formula` options. Validators are provided as an
object - the key is the `name` of the fields, and the value is another object containing the validators. Each
validator is has a key that is the name of the validation and a function that returns a string if the validation fails or
`null` if it passes. The string message is used to display the message, the key is added to the `errors` object in `validity`
```sveltehtml
<script>
import { formula } from 'svelte-formula'
import { calculateStrong } from '../libs/password'
const { form, formValues, submitValues, validity, touched, dirty, formValid } = formula({
validators: {
password: {
isStrong: (value: string) => calculateStrong(value) ? null : 'Your password is too weak'
},
'invoice-ids': {
invoicePrefix: (values: string[]) => values.every(value => value.startsWith('INV-')) ? null : 'Your invoice IDs must all begin with INV-'
}
}
});
</script>
...
<input type='password' name='password' required minlength='8' class:error={$touched?.password && $validity?.username?.invalid}/>
<div hidden={$validity?.password?.valid}>{$validity?.password?.message}</div>
<div hidden={$validity?.password?.errors?.isStrong}>You have a strong password!</div>
...
<input type='text' id='invoice-1' name='invoice-ids' />
<input type='text' id='invoice-2' name='invoice-ids' />
<input type='text' id='invoice-3' name='invoice-ids' />
<div hidden={$validity?.['invoice-ids']?.valid}>{$validity?.['invoice-ids']?.message}</div>
```
- Support for custom form-level validators via the `formValidators` property of the `formula` options. Validators are provided as an
object - Each validator has a key that is the name of the validation, and a function that returns a string if the validation fails or
`null` if it passes. The error message are available via the `formValidity` store.
```sveltehtml
<script>
import { formula } from 'svelte-formula'
const { form, submitValues, submitValidity, formValid } = formula({
formValidators: {
passwordsMatch: (values) => values.password === values.passwordMatch ? null : 'Your passwords must match'
}
})
</script>
<input type='password' name='password' required minlength='8'>
<input type='password' name='passwordMatch' required minlength='8'>
<div hidden="{!$submitValidity?.passwordsMatch}">{$submitValidity?.passwordsMatch}</div>
```
### Fixed
- Correctly pass options to form creation
## [0.1.1] 2021-02-15

@@ -18,3 +82,4 @@

the same name REQUIRES and `id` property. Returns an array sorted on the `id` property alphabetically in the users'
locale if detected (and always falls back to `en` if not), if this needs to be overridden it can be passed to the formula constructor (e.g `formula({ locale: 'de' });`)
locale if detected (and always falls back to `en` if not), if this needs to be overridden it can be passed to the
formula constructor (e.g `formula({ locale: 'de' });`)

@@ -21,0 +86,0 @@ ## [0.1.0] 2021-02-15

@@ -31,2 +31,7 @@ import { Writable } from 'svelte/store';

/**
* Store containing form-level validity if providing custom validators for the entire form
* @typedef Writable<Record<string, string>>
*/
formValidity: Writable<Record<string, string>>;
/**
* Store with the current touched state of elements

@@ -50,3 +55,3 @@ * @typedef Writable<Record<string, boolean>>

*/
formValid: Writable<boolean>;
isFormValid: Writable<boolean>;
};

@@ -1,2 +0,4 @@

import { FormEl } from '../types/forms';
import { Writable } from 'svelte/store';
import { FormEl, FormValues } from '../types/forms';
import { ValidationRules } from '../types/validation';
/**

@@ -8,1 +10,20 @@ * Extract the errors from the element validity - as it's not enumerable, it cannot be

export declare function extractErrors(el: FormEl): Record<string, boolean>;
/**
* Do form level validations
* @param formValues
* @param formValidity
* @param isFormValid
* @param customValidators
*/
export declare function checkFormValidity(formValues: Writable<FormValues>, formValidity: Writable<Record<string, string>>, isFormValid: Writable<boolean>, customValidators: ValidationRules): () => void;
/**
* Check the validity of a field and against custom validators
* @param el
* @param value
* @param customValidators
*/
export declare function checkValidity(el: FormEl, value: unknown | unknown[], customValidators?: ValidationRules): {
valid: boolean;
message: string;
errors: Record<string, boolean>;
};

19

lib/event.d.ts
import { Writable } from 'svelte/store';
import { FormErrors } from '../types/forms';
import { ValidationRules } from '../types/validation';
/**

@@ -9,4 +10,5 @@ * Create a generic value event handler

* @param updateMultiple
* @param customValidators
*/
export declare function createValueHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>, updateMultiple?: any): (event: KeyboardEvent | MouseEvent) => void;
export declare function createValueHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>, updateMultiple?: any, customValidators?: ValidationRules): (event: KeyboardEvent | MouseEvent) => void;
/**

@@ -18,4 +20,5 @@ * Create a handler for checkbox elements

* @param isValid
* @param customValidators
*/
export declare function createCheckHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>, updateMultiple?: any): (event: KeyboardEvent | MouseEvent) => void;
export declare function createCheckHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>, updateMultiple?: any, customValidators?: ValidationRules): (event: KeyboardEvent | MouseEvent) => void;
/**

@@ -26,4 +29,5 @@ * Create a handler for radio elements

* @param isValid
* @param customValidators
*/
export declare function createRadioHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>): (event: KeyboardEvent | MouseEvent) => void;
export declare function createRadioHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>, customValidators?: ValidationRules): (event: KeyboardEvent | MouseEvent) => void;
/**

@@ -34,10 +38,11 @@ * Create a handler for select elements

* @param isValid
* @param customValidators
*/
export declare function createSelectHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>): (event: KeyboardEvent | MouseEvent) => void;
export declare function createSelectHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>, customValidators?: ValidationRules): (event: KeyboardEvent | MouseEvent) => void;
/**
* Create a handler for a form element submission, when called it copies the contents
* of the current value store to the submit store and then unsubscribes
* @param values
* @param submit
* @param formValues
* @param submitValues
*/
export declare function createSubmitHandler(values: Writable<Record<string, unknown>>, submit: Writable<Record<string, unknown>>): () => void;
export declare function createSubmitHandler(formValues: Writable<Record<string, unknown>>, submitValues: Writable<Record<string, unknown>>): () => void;
import { ExtractedFormInfo, FormEl } from '../types/forms';
import { ValidationRules } from '../types/validation';
/**

@@ -7,10 +8,5 @@ * Generic handle for extracting data from an `<input>` or `<textarea>` element that

* @param updateMultiple
* @param customValidators
*/
export declare function extractData(el: FormEl, updateMultiple?: any): {
name: string;
value: any;
valid: boolean;
message: string;
errors: Record<string, boolean>;
};
export declare function extractData(el: FormEl, updateMultiple?: any, customValidators?: ValidationRules): ExtractedFormInfo;
/**

@@ -21,4 +17,5 @@ * Extract the data from an `<input type="checkbox"> element - this returns a boolean value

* @param updateMultiple
* @param customValidators
*/
export declare function extractCheckbox(el: HTMLInputElement, updateMultiple?: any): ExtractedFormInfo;
export declare function extractCheckbox(el: HTMLInputElement, updateMultiple?: any, customValidators?: ValidationRules): ExtractedFormInfo;
/**

@@ -29,4 +26,5 @@ * Extract the data from an `<input type="radio">` element, returning the value

* @param el
* @param customValidators
*/
export declare function extractRadio(el: HTMLInputElement): ExtractedFormInfo;
export declare function extractRadio(el: HTMLInputElement, customValidators?: ValidationRules): ExtractedFormInfo;
/**

@@ -36,3 +34,4 @@ * Extract the data from a `<select>` element - here we can support single values

* @param el
* @param customValidators
*/
export declare function extractSelect(el: HTMLSelectElement): ExtractedFormInfo;
export declare function extractSelect(el: HTMLSelectElement, customValidators?: ValidationRules): ExtractedFormInfo;
import { FormErrors, FormValues } from '../types/forms';
import { Writable } from 'svelte/store';
import { FormulaOptions } from '../types/options';
export declare function createForm(values: Writable<FormValues>, submit: Writable<FormValues>, errors: Writable<FormErrors>, isValid: Writable<boolean>, touched: Writable<Record<string, boolean>>, dirty: Writable<Record<string, boolean>>, options?: FormulaOptions): (node: HTMLElement) => {
export declare function createForm({ formValues, submitValues, formValidity, validity, isFormValid, touched, dirty, options, }: {
formValues: Writable<FormValues>;
submitValues: Writable<FormValues>;
formValidity: Writable<Record<string, string>>;
validity: Writable<FormErrors>;
isFormValid: Writable<boolean>;
touched: Writable<Record<string, boolean>>;
dirty: Writable<Record<string, boolean>>;
options?: FormulaOptions;
}): (node: HTMLElement) => {
destroy: () => void;
};
{
"name": "svelte-formula",
"description": "Reactive Forms for Svelte",
"version": "0.1.1",
"version": "0.2.0",
"keywords": [

@@ -6,0 +6,0 @@ "svelte",

@@ -25,5 +25,7 @@ # Svelte Formula

| Options | Type | Description |
| ------- | -------- | ----------------------------------------------------------- |
| `local` | `string` | Optional locale for `id` sorting when using multiple values |
| Options | Type | Description |
| ---------------- | -------- | ----------------------------------------------------------- |
| `locale` | `String` | Optional locale for `id` sorting when using multiple values |
| `validators` | `Object` | An object containing custom validators for fields |
| `formValidators` | `Object` | An object containing custom validators for the form |

@@ -39,3 +41,2 @@ ### Example

<script>
import { onDestroy } from 'svelte'
import { formula } from 'svelte-formula'

@@ -45,4 +46,4 @@

const valueSub = formValues.subscribe(values => console.log(values));
const submitSub = submitValues.subscribe(values => console.log(values));
$: console.log($formValues);
$: console.log($submitValues);

@@ -53,6 +54,2 @@ function handleSubmit(e) {

onDestroy(() => {
valueSub();
submitSub();
})

@@ -64,3 +61,3 @@ </script>

<label for='username'>Username</label>
<input type='text' id='username' name='username' required />
<input type='email' id='username' name='username' required />
<span hidden={$validity?.username?.valid}>{ validity?.username?.message }</span>

@@ -80,2 +77,7 @@ </div>

### dirty
A store that updates when fields are marked as dirty - contains an `Object` with key/value of `name` and `Boolean`,
fields where the user triggers a `blur` event, and the value has been changed from the original value.
### formValues

@@ -87,2 +89,12 @@

### formValidity
A store that contains a key/value `Object` of errors on the form when using `formValidators` - unlike `validity` this only
contains a key and string value that is any message from the validator.
### isFormValid
A store that is a single `Boolean` value if the form is currently valid or not - this only emits when the form validity
changes
### submitValues

@@ -93,2 +105,7 @@

### touched
A store that updates when fields are marked as touched - contains an `Object` with key/value of `name` and `Boolean`,
fields are added to the store as they receive a `focus` event
### validity

@@ -104,19 +121,72 @@

the [Constraint Validation](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#the_constraint_validation_api)
API.
API, or from custom `validators` in the options.
### formValid
## Custom Field Validations
A store that is a single `Boolean` value if the form is currently valid or not - this only emits when the form validity
changes
While Formula is zero-config and uses validations set on the HTML5 elements, it's also possible to provide custom
validations via the `validations` option passed to `formula` on initialisation.
### touched
First provide the `name` of the field as a key, and the value as an `Object` - each object contains further keys per
error, and a function that does the validation - the result should be a `string` if it fails, or `null` if it passes.
A store that updates when fields are marked as touched - contains an `Object` with key/value of `name` and `Boolean`,
fields are added to the store as they receive a `focus` event
Custom validation will always be added to the `errors` object, however the `message` - HTML validations will always take
precedence over custom validations (e.g. `<input required>` message will always be before your custom message)
### dirty
Custom validators also support multi-value fields, but in this case an `id` must be set on each field to provide
uniqueness (such as an index value)
A store that updates when fields are marked as dirty - contains an `Object` with key/value of `name` and `Boolean`,
fields where the user triggers a `blur` event, and the value has been changed from the original value.
### Example
```sveltehtml
<script>
import { formula } from 'svelte-formula';
const { form, validity } = formula({
validators: {
username: {
inCompanyDomain: (value: string) => value.includes('@ourdomain.com') ? null : 'Your username must contain an @ourdomain.com email'
},
invoiceIds: {
validInvoiceId: (values: string[]) => values.every(value => value.startsWith('INV-')) ? null : 'Your invoice IDs must start with INV-'
}
}
});
</script>
<form use:form>
<label for='signup-username'>Username</label>
<input type='email' name='username' id='signup-username' required minlength='8' />
<div hidden={$validity?.email?.valid}>{$validity?.email?.message}</div>
<input type='text' name='invoiceIds' id='1' />
<input type='text' name='invoiceIds' id='2' />
<input type='text' name='invoiceIds' id='3' />
</form>
```
## Form Level Validation
Form level validations can also be provided using `formValidators` - these validators are passed the entire set of
values and allow for validation across different values (like password matching, or ensuring a selection is made in one
field based on another form field condition)
```sveltehtml
<script>
import { formula } from 'svelte-formula';
const { form, formValidity } = formula({
formValidators: {
passwordsMatch: (values) => values.password === values.passwordMatch ? null : 'Your passwords must match'
}
})
</script>
<form use:form>
<input type='password' name='password' required minlength='8'>
<input type='password' name='passwordMatch' required minlength='8'>
<div hidden={!$formValidity?.passwordsMatch}>{$formValidity?.passwordsMatch}</div>
</form>
```
## Roadmap

@@ -139,2 +209,7 @@

### Validation
- [x] Custom field-level validation via `formula` options
- [x] Custom form-level validation via `formula` options
### Other Items

@@ -141,0 +216,0 @@

@@ -94,2 +94,3 @@ import { writable } from 'svelte/store';

*/
function extractErrors(el) {

@@ -109,14 +110,46 @@ var output = {};

}
/**
* Do form level validations
* @param formValues
* @param formValidity
* @param isFormValid
* @param customValidators
*/
function checkFormValidity(formValues, formValidity, isFormValid, customValidators) {
return formValues.subscribe(function (values) {
formValidity.set({});
var validators = Object.entries(customValidators);
var _loop_1 = function _loop_1(i) {
var _a = __read(validators[i], 2),
name_1 = _a[0],
validator = _a[1];
var invalid = validator(values);
if (invalid) {
formValidity.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[name_1] = invalid, _a));
});
isFormValid.set(false);
}
};
for (var i = 0; i < validators.length; i++) {
_loop_1(i);
}
});
}
/**
* Generic handle for extracting data from an `<input>` or `<textarea>` element that
* doesn't have a special case
* Check the validity of a field and against custom validators
* @param el
* @param updateMultiple
* @param value
* @param customValidators
*/
function extractData(el, updateMultiple) {
return {
name: el.getAttribute('name'),
value: updateMultiple ? updateMultiple(el.id, el.value) : el.value,
function checkValidity(el, value, customValidators) {
var result = {
valid: el.checkValidity(),

@@ -126,4 +159,48 @@ message: el.validationMessage,

};
if (customValidators) {
var validators = Object.entries(customValidators);
for (var i = 0; i < validators.length; i++) {
var _a = __read(validators[i], 2),
name_2 = _a[0],
validator = _a[1];
var message = validator(value);
if (message === null) {
continue;
}
if (result.valid === true) {
result.valid = false;
result.message = message;
result.errors[name_2] = true;
} else {
result.errors[name_2] = true;
}
}
}
return result;
}
/**
* Generic handle for extracting data from an `<input>` or `<textarea>` element that
* doesn't have a special case
* @param el
* @param updateMultiple
* @param customValidators
*/
function extractData(el, updateMultiple, customValidators) {
var name = el.getAttribute('name');
var value = updateMultiple ? updateMultiple(el.id, el.value) : el.value;
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
}
/**
* Extract the data from an `<input type="checkbox"> element - this returns a boolean value

@@ -133,12 +210,13 @@ * if a single checkbox. If multiple checkboxes are detected it returns an array value

* @param updateMultiple
* @param customValidators
*/
function extractCheckbox(el, updateMultiple) {
return {
name: el.getAttribute('name'),
value: updateMultiple ? updateMultiple(el.checked, el.value) : el.checked,
valid: el.checkValidity(),
message: el.validationMessage,
errors: extractErrors(el)
};
function extractCheckbox(el, updateMultiple, customValidators) {
var name = el.getAttribute('name');
var value = updateMultiple ? updateMultiple(el.checked, el.value) : el.checked;
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
}

@@ -150,12 +228,13 @@ /**

* @param el
* @param customValidators
*/
function extractRadio(el) {
return {
name: el.getAttribute('name'),
value: el.checked ? el.value : '',
valid: el.checkValidity(),
message: el.validationMessage,
errors: extractErrors(el)
};
function extractRadio(el, customValidators) {
var name = el.getAttribute('name');
var value = el.checked ? el.value : '';
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
}

@@ -166,5 +245,6 @@ /**

* @param el
* @param customValidators
*/
function extractSelect(el) {
function extractSelect(el, customValidators) {
/**

@@ -187,9 +267,9 @@ * As the `HTMLCollectionOf` is not iterable, we have to loop over it with

return {
name: el.getAttribute('name'),
value: el.multiple ? getMultiValue(el.selectedOptions) : el.value,
valid: el.checkValidity(),
message: el.validationMessage,
errors: extractErrors(el)
};
var name = el.getAttribute('name');
var value = el.multiple ? getMultiValue(el.selectedOptions) : el.value;
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
}

@@ -233,9 +313,10 @@

* @param updateMultiple
* @param customValidators
*/
function createValueHandler(values, errors, isValid, updateMultiple) {
function createValueHandler(values, errors, isValid, updateMultiple, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractData(el, updateMultiple);
var details = extractData(el, updateMultiple, customValidators);
valueUpdate(details, values, errors, isValid);

@@ -250,8 +331,9 @@ };

* @param isValid
* @param customValidators
*/
function createCheckHandler(values, errors, isValid, updateMultiple) {
function createCheckHandler(values, errors, isValid, updateMultiple, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractCheckbox(el, updateMultiple);
var details = extractCheckbox(el, updateMultiple, customValidators);
valueUpdate(details, values, errors, isValid);

@@ -265,8 +347,9 @@ };

* @param isValid
* @param customValidators
*/
function createRadioHandler(values, errors, isValid) {
function createRadioHandler(values, errors, isValid, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractRadio(el);
var details = extractRadio(el, customValidators);
valueUpdate(details, values, errors, isValid);

@@ -280,8 +363,9 @@ };

* @param isValid
* @param customValidators
*/
function createSelectHandler(values, errors, isValid) {
function createSelectHandler(values, errors, isValid, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractSelect(el);
var details = extractSelect(el, customValidators);
valueUpdate(details, values, errors, isValid);

@@ -293,10 +377,10 @@ };

* of the current value store to the submit store and then unsubscribes
* @param values
* @param submit
* @param formValues
* @param submitValues
*/
function createSubmitHandler(values, submit) {
function createSubmitHandler(formValues, submitValues) {
return function () {
return values.subscribe(function (v) {
return submit.set(v);
return formValues.subscribe(function (v) {
return submitValues.set(v);
})();

@@ -526,44 +610,54 @@ };

function createForm(values, submit, errors, isValid, touched, dirty, options) {
function createForm(_a) {
var formValues = _a.formValues,
submitValues = _a.submitValues,
formValidity = _a.formValidity,
validity = _a.validity,
isFormValid = _a.isFormValid,
touched = _a.touched,
dirty = _a.dirty,
options = _a.options;
return function form(node) {
var keyupHandlers = new Map();
var changeHandlers = new Map();
var submitHander = undefined;
var submitHandler = undefined;
var formElements = getAllFieldsWithValidity(node);
formElements.forEach(function (el) {
// Create a single touch handler for each element, this is removed after it has first been focused
var _a; // Create a single touch handler for each element, this is removed after it has first been focused
createTouchHandler(el, touched);
createInitialValues(el, formElements, values, errors, touched);
createDirtyHandler(el, dirty, values);
createInitialValues(el, formElements, formValues, validity, touched);
createDirtyHandler(el, dirty, formValues);
var name = el.getAttribute('name');
var customValidations = (_a = options === null || options === void 0 ? void 0 : options.validators) === null || _a === void 0 ? void 0 : _a[name];
if (el instanceof HTMLSelectElement) {
var handler = createSelectHandler(values, errors, isValid);
var handler = createSelectHandler(formValues, validity, isFormValid, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else if (el.type === 'radio') {
var handler = createRadioHandler(values, errors, isValid);
var handler = createRadioHandler(formValues, validity, isFormValid, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else if (el.type === 'checkbox') {
var name_1 = el.getAttribute('name');
var isMultiple = isMultiCheckbox(name_1, formElements);
var isMultiple = isMultiCheckbox(name, formElements);
var updateMultiple = void 0;
if (isMultiple) {
updateMultiple = checkboxMultiUpdate(name_1);
updateMultiple = checkboxMultiUpdate(name);
}
var handler = createCheckHandler(values, errors, isValid, updateMultiple);
var handler = createCheckHandler(formValues, validity, isFormValid, updateMultiple, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else {
var name_2 = el.getAttribute('name');
var isMultiple = hasMultipleNames(name_2, formElements);
var isMultiple = hasMultipleNames(name, formElements);
var updateMultiple = void 0;
if (isMultiple) {
updateMultiple = inputMultiUpdate(name_2, options === null || options === void 0 ? void 0 : options.locale);
updateMultiple = inputMultiUpdate(name, options === null || options === void 0 ? void 0 : options.locale);
}
var handler = createValueHandler(values, errors, isValid, updateMultiple);
var handler = createValueHandler(formValues, validity, isFormValid, updateMultiple, customValidations);
el.addEventListener('keyup', handler);

@@ -574,6 +668,11 @@ keyupHandlers.set(el, handler);

var unsub = function unsub() {};
if (options === null || options === void 0 ? void 0 : options.formValidators) {
unsub = checkFormValidity(formValues, formValidity, isFormValid, options.formValidators);
}
if (node instanceof HTMLFormElement) {
var submitHandler = createSubmitHandler(values, submit);
submitHandler = createSubmitHandler(formValues, submitValues);
node.addEventListener('submit', submitHandler);
submitHander = submitHandler;
}

@@ -583,2 +682,4 @@

destroy: function destroy() {
unsub();
__spread(keyupHandlers).forEach(function (_a) {

@@ -600,4 +701,4 @@ var _b = __read(_a, 2),

if (submitHander) {
node.removeEventListener('submit', submitHander);
if (submitHandler) {
node.removeEventListener('submit', submitHandler);
}

@@ -623,3 +724,4 @@ }

var validity = writable({});
var formValid = writable(false);
var formValidity = writable({});
var isFormValid = writable(false);
return {

@@ -630,3 +732,12 @@ /**

*/
form: createForm(formValues, submitValues, validity, formValid, touched, dirty),
form: createForm({
formValues: formValues,
submitValues: submitValues,
formValidity: formValidity,
validity: validity,
isFormValid: isFormValid,
touched: touched,
dirty: dirty,
options: options
}),

@@ -646,2 +757,8 @@ /**

/**
* Store containing form-level validity if providing custom validators for the entire form
* @typedef Writable<Record<string, string>>
*/
formValidity: formValidity,
/**
* Store with the current touched state of elements

@@ -668,3 +785,3 @@ * @typedef Writable<Record<string, boolean>>

*/
formValid: formValid
isFormValid: isFormValid
};

@@ -671,0 +788,0 @@ }

@@ -98,2 +98,3 @@ (function (global, factory) {

*/
function extractErrors(el) {

@@ -113,14 +114,46 @@ var output = {};

}
/**
* Do form level validations
* @param formValues
* @param formValidity
* @param isFormValid
* @param customValidators
*/
function checkFormValidity(formValues, formValidity, isFormValid, customValidators) {
return formValues.subscribe(function (values) {
formValidity.set({});
var validators = Object.entries(customValidators);
var _loop_1 = function _loop_1(i) {
var _a = __read(validators[i], 2),
name_1 = _a[0],
validator = _a[1];
var invalid = validator(values);
if (invalid) {
formValidity.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[name_1] = invalid, _a));
});
isFormValid.set(false);
}
};
for (var i = 0; i < validators.length; i++) {
_loop_1(i);
}
});
}
/**
* Generic handle for extracting data from an `<input>` or `<textarea>` element that
* doesn't have a special case
* Check the validity of a field and against custom validators
* @param el
* @param updateMultiple
* @param value
* @param customValidators
*/
function extractData(el, updateMultiple) {
return {
name: el.getAttribute('name'),
value: updateMultiple ? updateMultiple(el.id, el.value) : el.value,
function checkValidity(el, value, customValidators) {
var result = {
valid: el.checkValidity(),

@@ -130,4 +163,48 @@ message: el.validationMessage,

};
if (customValidators) {
var validators = Object.entries(customValidators);
for (var i = 0; i < validators.length; i++) {
var _a = __read(validators[i], 2),
name_2 = _a[0],
validator = _a[1];
var message = validator(value);
if (message === null) {
continue;
}
if (result.valid === true) {
result.valid = false;
result.message = message;
result.errors[name_2] = true;
} else {
result.errors[name_2] = true;
}
}
}
return result;
}
/**
* Generic handle for extracting data from an `<input>` or `<textarea>` element that
* doesn't have a special case
* @param el
* @param updateMultiple
* @param customValidators
*/
function extractData(el, updateMultiple, customValidators) {
var name = el.getAttribute('name');
var value = updateMultiple ? updateMultiple(el.id, el.value) : el.value;
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
}
/**
* Extract the data from an `<input type="checkbox"> element - this returns a boolean value

@@ -137,12 +214,13 @@ * if a single checkbox. If multiple checkboxes are detected it returns an array value

* @param updateMultiple
* @param customValidators
*/
function extractCheckbox(el, updateMultiple) {
return {
name: el.getAttribute('name'),
value: updateMultiple ? updateMultiple(el.checked, el.value) : el.checked,
valid: el.checkValidity(),
message: el.validationMessage,
errors: extractErrors(el)
};
function extractCheckbox(el, updateMultiple, customValidators) {
var name = el.getAttribute('name');
var value = updateMultiple ? updateMultiple(el.checked, el.value) : el.checked;
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
}

@@ -154,12 +232,13 @@ /**

* @param el
* @param customValidators
*/
function extractRadio(el) {
return {
name: el.getAttribute('name'),
value: el.checked ? el.value : '',
valid: el.checkValidity(),
message: el.validationMessage,
errors: extractErrors(el)
};
function extractRadio(el, customValidators) {
var name = el.getAttribute('name');
var value = el.checked ? el.value : '';
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
}

@@ -170,5 +249,6 @@ /**

* @param el
* @param customValidators
*/
function extractSelect(el) {
function extractSelect(el, customValidators) {
/**

@@ -191,9 +271,9 @@ * As the `HTMLCollectionOf` is not iterable, we have to loop over it with

return {
name: el.getAttribute('name'),
value: el.multiple ? getMultiValue(el.selectedOptions) : el.value,
valid: el.checkValidity(),
message: el.validationMessage,
errors: extractErrors(el)
};
var name = el.getAttribute('name');
var value = el.multiple ? getMultiValue(el.selectedOptions) : el.value;
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
}

@@ -237,9 +317,10 @@

* @param updateMultiple
* @param customValidators
*/
function createValueHandler(values, errors, isValid, updateMultiple) {
function createValueHandler(values, errors, isValid, updateMultiple, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractData(el, updateMultiple);
var details = extractData(el, updateMultiple, customValidators);
valueUpdate(details, values, errors, isValid);

@@ -254,8 +335,9 @@ };

* @param isValid
* @param customValidators
*/
function createCheckHandler(values, errors, isValid, updateMultiple) {
function createCheckHandler(values, errors, isValid, updateMultiple, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractCheckbox(el, updateMultiple);
var details = extractCheckbox(el, updateMultiple, customValidators);
valueUpdate(details, values, errors, isValid);

@@ -269,8 +351,9 @@ };

* @param isValid
* @param customValidators
*/
function createRadioHandler(values, errors, isValid) {
function createRadioHandler(values, errors, isValid, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractRadio(el);
var details = extractRadio(el, customValidators);
valueUpdate(details, values, errors, isValid);

@@ -284,8 +367,9 @@ };

* @param isValid
* @param customValidators
*/
function createSelectHandler(values, errors, isValid) {
function createSelectHandler(values, errors, isValid, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractSelect(el);
var details = extractSelect(el, customValidators);
valueUpdate(details, values, errors, isValid);

@@ -297,10 +381,10 @@ };

* of the current value store to the submit store and then unsubscribes
* @param values
* @param submit
* @param formValues
* @param submitValues
*/
function createSubmitHandler(values, submit) {
function createSubmitHandler(formValues, submitValues) {
return function () {
return values.subscribe(function (v) {
return submit.set(v);
return formValues.subscribe(function (v) {
return submitValues.set(v);
})();

@@ -530,44 +614,54 @@ };

function createForm(values, submit, errors, isValid, touched, dirty, options) {
function createForm(_a) {
var formValues = _a.formValues,
submitValues = _a.submitValues,
formValidity = _a.formValidity,
validity = _a.validity,
isFormValid = _a.isFormValid,
touched = _a.touched,
dirty = _a.dirty,
options = _a.options;
return function form(node) {
var keyupHandlers = new Map();
var changeHandlers = new Map();
var submitHander = undefined;
var submitHandler = undefined;
var formElements = getAllFieldsWithValidity(node);
formElements.forEach(function (el) {
// Create a single touch handler for each element, this is removed after it has first been focused
var _a; // Create a single touch handler for each element, this is removed after it has first been focused
createTouchHandler(el, touched);
createInitialValues(el, formElements, values, errors, touched);
createDirtyHandler(el, dirty, values);
createInitialValues(el, formElements, formValues, validity, touched);
createDirtyHandler(el, dirty, formValues);
var name = el.getAttribute('name');
var customValidations = (_a = options === null || options === void 0 ? void 0 : options.validators) === null || _a === void 0 ? void 0 : _a[name];
if (el instanceof HTMLSelectElement) {
var handler = createSelectHandler(values, errors, isValid);
var handler = createSelectHandler(formValues, validity, isFormValid, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else if (el.type === 'radio') {
var handler = createRadioHandler(values, errors, isValid);
var handler = createRadioHandler(formValues, validity, isFormValid, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else if (el.type === 'checkbox') {
var name_1 = el.getAttribute('name');
var isMultiple = isMultiCheckbox(name_1, formElements);
var isMultiple = isMultiCheckbox(name, formElements);
var updateMultiple = void 0;
if (isMultiple) {
updateMultiple = checkboxMultiUpdate(name_1);
updateMultiple = checkboxMultiUpdate(name);
}
var handler = createCheckHandler(values, errors, isValid, updateMultiple);
var handler = createCheckHandler(formValues, validity, isFormValid, updateMultiple, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else {
var name_2 = el.getAttribute('name');
var isMultiple = hasMultipleNames(name_2, formElements);
var isMultiple = hasMultipleNames(name, formElements);
var updateMultiple = void 0;
if (isMultiple) {
updateMultiple = inputMultiUpdate(name_2, options === null || options === void 0 ? void 0 : options.locale);
updateMultiple = inputMultiUpdate(name, options === null || options === void 0 ? void 0 : options.locale);
}
var handler = createValueHandler(values, errors, isValid, updateMultiple);
var handler = createValueHandler(formValues, validity, isFormValid, updateMultiple, customValidations);
el.addEventListener('keyup', handler);

@@ -578,6 +672,11 @@ keyupHandlers.set(el, handler);

var unsub = function unsub() {};
if (options === null || options === void 0 ? void 0 : options.formValidators) {
unsub = checkFormValidity(formValues, formValidity, isFormValid, options.formValidators);
}
if (node instanceof HTMLFormElement) {
var submitHandler = createSubmitHandler(values, submit);
submitHandler = createSubmitHandler(formValues, submitValues);
node.addEventListener('submit', submitHandler);
submitHander = submitHandler;
}

@@ -587,2 +686,4 @@

destroy: function destroy() {
unsub();
__spread(keyupHandlers).forEach(function (_a) {

@@ -604,4 +705,4 @@ var _b = __read(_a, 2),

if (submitHander) {
node.removeEventListener('submit', submitHander);
if (submitHandler) {
node.removeEventListener('submit', submitHandler);
}

@@ -627,3 +728,4 @@ }

var validity = store.writable({});
var formValid = store.writable(false);
var formValidity = store.writable({});
var isFormValid = store.writable(false);
return {

@@ -634,3 +736,12 @@ /**

*/
form: createForm(formValues, submitValues, validity, formValid, touched, dirty),
form: createForm({
formValues: formValues,
submitValues: submitValues,
formValidity: formValidity,
validity: validity,
isFormValid: isFormValid,
touched: touched,
dirty: dirty,
options: options
}),

@@ -650,2 +761,8 @@ /**

/**
* Store containing form-level validity if providing custom validators for the entire form
* @typedef Writable<Record<string, string>>
*/
formValidity: formValidity,
/**
* Store with the current touched state of elements

@@ -672,3 +789,3 @@ * @typedef Writable<Record<string, boolean>>

*/
formValid: formValid
isFormValid: isFormValid
};

@@ -675,0 +792,0 @@ }

@@ -0,1 +1,2 @@

import { CustomValidationRules, ValidationRules } from './validation';
/**

@@ -9,2 +10,10 @@ * Optional settings for Formula

locale?: string;
/**
* Custom Validators for fields
*/
validators?: CustomValidationRules;
/**
* Validation rules for the entire form
*/
formValidators?: ValidationRules;
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc