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.4.0 to 0.5.0

31

CHANGELOG.md

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

## [0.5.0] 2021-02-17
### Added
- New [Documentation Site](https://tanepiper.github.io/svelte-formula/) (still WIP)
- Added `messages` option to FormulaOptions, this is a key/value `Object` for setting custom validation messages per
error:
```sveltehtml
<script>
import { formula } from 'svelte-formula'
const { form } = formula({
messages: {
valueMissing: 'Debes ingresar un nombre de usuario'
}
})
</script>
```
- Add support for validation messages via HTML `data-*` attributes - these will always take presidency over items in
the `messages` object if both exist. The format of the name should be a hyphen before any characters that are
uppercase (e.g `valueMissing` becomes `data-value-missing`)
```html
<input type="text" name="postcode" required data-value-missing="Bitte geben Sie Ihre Postleitzahl ein" />
```
### Changed
- More internal performance improvements
## [0.4.0] 2021-02-16

@@ -10,0 +41,0 @@

15

lib/errors.d.ts
import { FormEl } from '../types/forms';
import { ValidationRules } from '../types/validation';
import { ValidationRule } from '../types/validation';
import { FormulaStores } from '../types/formula';
import { FormulaOptions } from '../types/options';
/**

@@ -8,4 +9,5 @@ * Extract the errors from the element validity - as it's not enumerable, it cannot be

* @param el
* @param custom
*/
export declare function extractErrors(el: FormEl): Record<string, boolean>;
export declare function extractErrors(el: FormEl, custom?: Record<string, boolean>): Record<string, boolean>;
/**

@@ -16,10 +18,9 @@ * Do form level validations

*/
export declare function checkFormValidity(stores: FormulaStores, customValidators: ValidationRules): () => void;
export declare function checkFormValidity(stores: FormulaStores, customValidators: ValidationRule): () => void;
/**
* Check the validity of a field and against custom validators
* @param el
* @param value
* @param customValidators
* @param name
* @param options
*/
export declare function checkValidity(el: FormEl, value: unknown | unknown[], customValidators?: ValidationRules): {
export declare function checkValidity(name: string, options?: FormulaOptions): (el: FormEl, value: unknown | unknown[]) => {
valid: boolean;

@@ -26,0 +27,0 @@ message: string;

import { FormEl } from '../types/forms';
import { ValidationRules } from '../types/validation';
import { FormulaStores } from '../types/formula';
export declare function createHandler(name: string, eventName: string, element: FormEl, groupElements: FormEl[], stores: FormulaStores, customValidators?: ValidationRules): () => void;
import { FormulaOptions } from 'packages/svelte/formula/src/types/options';
/**
* Create a handler for the passed element
* @param name
* @param eventName
* @param element
* @param groupElements
* @param stores
* @param options
*/
export declare function createHandler(name: string, eventName: string, element: FormEl, groupElements: FormEl[], stores: FormulaStores, options: FormulaOptions): () => void;
/**
* Create a handler for a form element submission, when called it copies the contents

@@ -7,0 +16,0 @@ * of the current value store to the submit store and then unsubscribes

@@ -1,44 +0,58 @@

import { ExtractedFormInfo, FormEl } from '../types/forms';
import { ValidationRules } from '../types/validation';
import { FormEl, FormulaField } from '../types/forms';
import { FormulaOptions } from 'packages/svelte/formula/src/types/options';
/**
* Generic handle for extracting data from an `<input>` or `<textarea>` element that
* doesn't have a special case
* Create a data handler for any type of input field
* @param name
* @param element
* @param groupElements
* @param customValidators
* @param elementGroup
* @param options
*/
export declare function extractData(name: string, element: FormEl, groupElements: FormEl[], customValidators?: ValidationRules): ExtractedFormInfo;
export declare function createFieldExtract(name: string, elementGroup: FormEl[], options: FormulaOptions): (element: HTMLInputElement) => FormulaField;
/**
* Extract the data from an `<input type="checkbox"> element - this returns a boolean value
* if a single checkbox. If multiple checkboxes are detected it returns an array value
* Create a data handler for checkbox fields
* @param name
* @param element The element being checked
* @param elements All elements from the name group
* @param customValidators
* @param elementGroup
* @param options
*/
export declare function extractCheckbox(name: string, element: HTMLInputElement, elements: HTMLInputElement[], customValidators?: ValidationRules): ExtractedFormInfo;
export declare function createCheckboxExtract(name: string, elementGroup: FormEl[], options: FormulaOptions): (element: HTMLInputElement) => {
valid: boolean;
message: string;
errors: Record<string, boolean>;
name: string;
value: boolean | string[];
};
/**
* Extract the data from an `<input type="radio">` element, returning the value
* only for checked items, this works both with initial values and when the user
* selects a value
* Create a data handler for radio groups
* @param name
* @param el
* @param customValidators
* @param options
*/
export declare function extractRadio(name: string, el: HTMLInputElement, customValidators?: ValidationRules): ExtractedFormInfo;
export declare function createRadioExtract(name: string, options: FormulaOptions): (element: HTMLInputElement) => {
valid: boolean;
message: string;
errors: Record<string, boolean>;
name: string;
value: string;
};
/**
* Extract the data from a `<select>` element - here we can support single values
* or if the field is multiple it will return an array of values
* Create a data handler for select fields
* @param name
* @param el
* @param customValidators
* @param options
*/
export declare function extractSelect(name: string, el: HTMLSelectElement, customValidators?: ValidationRules): ExtractedFormInfo;
export declare function createSelectExtract(name: string, options: FormulaOptions): (element: HTMLSelectElement) => {
valid: boolean;
message: string;
errors: Record<string, boolean>;
name: string;
value: string | any[];
};
/**
* Extract data from the files
* Create a data handler for form fields
* @param name
* @param el
* @param customValidators
* @param options
*/
export declare function extractFile(name: string, el: HTMLInputElement, customValidators?: ValidationRules): ExtractedFormInfo;
export declare function createFileExtract(name: string, options: FormulaOptions): (element: HTMLInputElement) => {
valid: boolean;
message: string;
errors: Record<string, boolean>;
name: string;
value: FileList;
};

@@ -11,13 +11,1 @@ import { FormEl } from '../types/forms';

export declare function getAllFieldsWithValidity(rootEl: HTMLElement): FormEl[];
/**
* Check if our checkbox has multiple values of the same name
* @param name
* @param elements
*/
export declare function isMultiCheckbox(name: string, elements: FormEl[]): boolean;
/**
* Check if our checkbox has multiple values of the same name
* @param name
* @param elements
*/
export declare function hasMultipleNames(name: string, elements: FormEl[]): boolean;
import { FormEl } from '../types/forms';
import { FormulaStores } from '../types/formula';
import { ValidationFn } from '../types/validation';
import { FormulaOptions } from '../types/options';
/**
* Create the initial value type for the current element, handling cases for multiple
* values
* @param name
* @param el
* @param groupElements
* @param stores
* @param customValidators
*/
export declare function createInitialValues(name: string, el: FormEl, groupElements: FormEl[], stores: FormulaStores, customValidators: Record<string, ValidationFn>): void;
/**
* Get the initial value from the passed elements

@@ -19,5 +9,5 @@ * @param name

* @param stores
* @param validations
* @param options
*/
export declare function getInitialValue(name: string, elements: FormEl[], stores: FormulaStores, validations: Record<string, ValidationFn>): void;
export declare function getInitialValue(name: string, elements: FormEl[], stores: FormulaStores, options: FormulaOptions): void;
/**

@@ -24,0 +14,0 @@ * Create the stores for the instance

{
"name": "svelte-formula",
"description": "Reactive Forms for Svelte",
"version": "0.4.0",
"version": "0.5.0",
"keywords": [

@@ -16,9 +16,9 @@ "svelte",

},
"homepage": "https://github.com/tanepiper/svelte-plugins/tree/main/packages/svelte/formula",
"homepage": "https://tanepiper.github.io/svelte-formula/",
"bugs": {
"url": "https://github.com/tanepiper/svelte-plugins/issues"
"url": "https://github.com/tanepiper/svelte-formula/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/tanepiper/svelte-plugins.git"
"url": "https://github.com/tanepiper/svelte-formula.git"
},

@@ -25,0 +25,0 @@ "peerDependencies": {

@@ -7,2 +7,4 @@ # Svelte Formula

[Documentation](https://tanepiper.github.io/svelte-formula/)
Formula is a zero-configuration reactive form library for Svelte, currently in early development.

@@ -26,7 +28,8 @@

| 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 |
| Options | Type | Description |
| ---------------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| `locale` | `String` | Optional locale for `id` sorting when using multiple values |
| `messages` | `Object` | An object containing a key for each named field and an object containing key/value text with replacement text |
| `validators` | `Object` | An object containing custom validators for fields |
| `formValidators` | `Object` | An object containing custom validators for the form |

@@ -87,4 +90,4 @@ ### Example

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.
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.

@@ -132,2 +135,14 @@ ### isFormValid

### Custom Validation Text & Localisation
It's possible to provide custom localisation or text for error messages, they can be provided two ways:
- In the `formula` constructor pass a `messages` object. They key should be the `name` of the field. The value is
an `Object` containing the key of the error the message is for, and the value the replacement string.
- In the HTML add a `data-*` property for the value, separating any camel case values with a hyphen (e.g. `valueMissing`
becomes `data-value-missing`)
In order of precedence, the data value will always come before the constructor options
### Example

@@ -141,2 +156,7 @@

const { form, validity } = formula({
messages: {
username: {
valueMissing: 'You really should put an email address in'
}
},
validators: {

@@ -154,3 +174,4 @@ username: {

<label for='signup-username'>Username</label>
<input type='email' name='username' id='signup-username' required minlength='8' />
<input type='email' name='username' id='signup-username' required minlength='8'
data-value-missing='This error message will appear before the one in the options' />
<div hidden={$validity?.email?.valid}>{$validity?.email?.message}</div>

@@ -210,8 +231,9 @@

- [x] Custom form-level validation via `formula` options
- [x] Support for localised messages for validation errors
### Other Items
- [ ] Add Unit Tests
- [ ] Add full documentation
- [ ] Add Unit Tests - IN PROGRESS
- [ ] Add full documentation - IN PROGRESS
Icon made by [Eucalyp](https://creativemarket.com/eucalyp) from [flaticon.com](https://www.flaticon.com)

@@ -83,5 +83,6 @@ import { writable } from 'svelte/store';

* @param el
* @param custom
*/
function extractErrors(el) {
function extractErrors(el, custom) {
var output = {};

@@ -98,3 +99,3 @@

return output;
return __assign(__assign({}, output), custom);
}

@@ -134,127 +135,158 @@ /**

}
function checkForCustomMessage(name, errors, el, options) {
var messages = options.messages;
var customMessages = messages && messages[name] || {};
var message;
var dataSet = el.dataset;
Object.keys(dataSet).forEach(function (key) {
if (errors[key]) {
message = dataSet[key];
}
});
if (!message) {
Object.keys(customMessages).forEach(function (key) {
if (errors[key]) {
message = customMessages[key];
}
});
}
return message;
}
/**
* Check the validity of a field and against custom validators
* @param el
* @param value
* @param customValidators
* @param name
* @param options
*/
function checkValidity(el, value, customValidators) {
var result = {
valid: el.checkValidity(),
message: el.validationMessage,
errors: extractErrors(el)
};
if ((value !== '' || value !== null) && customValidators) {
var validators = Object.entries(customValidators);
function checkValidity(name, options) {
/**
* Method called each time we want to do validity
*/
return function (el, value) {
el.setCustomValidity('');
for (var i = 0; i < validators.length; i++) {
var _a = __read(validators[i], 2),
name_2 = _a[0],
validator = _a[1];
if (!options) {
return {
valid: el.checkValidity(),
message: el.validationMessage,
errors: extractErrors(el)
};
}
var message = validator(value);
var validators = options.validators;
var customErrors = {}; // Handle custom validation
if (message === null) {
continue;
}
if ((value !== '' || value !== null) && validators && validators[name]) {
var customValidators = Object.entries(validators[name]);
if (result.valid === true) {
result.valid = false;
result.message = message;
result.errors[name_2] = true;
} else {
result.errors[name_2] = true;
for (var i = 0; i < customValidators.length; i++) {
var _a = __read(customValidators[i], 2),
name_2 = _a[0],
validator = _a[1];
var message_1 = validator(value);
if (message_1 !== null) {
if (!el.validationMessage) {
el.setCustomValidity(message_1);
}
customErrors[name_2] = true;
}
}
}
}
} // Check for any custom messages
return result;
var errors = extractErrors(el, customErrors);
var message = checkForCustomMessage(name, errors, el, options);
return {
valid: el.checkValidity(),
message: message || el.validationMessage,
errors: errors
};
};
}
/**
* Generic handle for extracting data from an `<input>` or `<textarea>` element that
* doesn't have a special case
* Create a data handler for any type of input field
* @param name
* @param element
* @param groupElements
* @param customValidators
* @param elementGroup
* @param options
*/
function extractData(name, element, groupElements, customValidators) {
var validValue = element.value === '' || typeof element.value === 'undefined' ? '' : element.value;
var value = groupElements.length > 1 ? groupElements.map(function (v) {
if (v.id === element.id) {
return validValue;
}
function createFieldExtract(name, elementGroup, options) {
var validator = checkValidity(name, options);
return function (element) {
var validValue = element.value === '' || element.value === null || typeof element.value === 'undefined' ? '' : element.value;
var value = elementGroup.length > 1 ? elementGroup.map(function (v) {
return v.id === element.id ? validValue : v.value;
}).filter(function (v) {
return v !== '';
}) : validValue; // Parse number values
return v.value;
}).filter(function (v) {
return v !== '';
}) : validValue;
if (['number', 'range'].includes(element.getAttribute('type'))) {
if (Array.isArray(value)) {
value = value.length > 0 ? value.map(function (v) {
return parseFloat(v);
}) : [];
} else {
value = value !== '' ? parseFloat(value) : null;
if (['number', 'range'].includes(element.getAttribute('type'))) {
if (Array.isArray(value)) {
value = value.length > 0 ? value.map(function (v) {
return parseFloat(v);
}) : [];
} else {
value = value !== '' ? parseFloat(value) : null;
}
}
}
var validity = checkValidity(element, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
return __assign({
name: name,
value: value
}, validator(element, value));
};
}
/**
* Extract the data from an `<input type="checkbox"> element - this returns a boolean value
* if a single checkbox. If multiple checkboxes are detected it returns an array value
* Create a data handler for checkbox fields
* @param name
* @param element The element being checked
* @param elements All elements from the name group
* @param customValidators
* @param elementGroup
* @param options
*/
function extractCheckbox(name, element, elements, customValidators) {
var value = elements.length > 1 ? elements.map(function (e) {
return e.id === element.id ? element.checked && element.value || null : e.checked && e.value || null;
}).filter(function (v) {
return v !== null;
}) : element.checked;
var validity = checkValidity(element, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
function createCheckboxExtract(name, elementGroup, options) {
var validator = checkValidity(name, options);
return function (element) {
var value = elementGroup.length > 1 ? elementGroup.map(function (e) {
return e.id === element.id ? element.checked && element.value || null : e.checked && e.value || null;
}).filter(function (v) {
return v !== null;
}) : element.checked;
return __assign({
name: name,
value: value
}, validator(element, value));
};
}
/**
* Extract the data from an `<input type="radio">` element, returning the value
* only for checked items, this works both with initial values and when the user
* selects a value
* Create a data handler for radio groups
* @param name
* @param el
* @param customValidators
* @param options
*/
function extractRadio(name, el, customValidators) {
var value = el.checked ? el.value : '';
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
function createRadioExtract(name, options) {
var validator = checkValidity(name, options);
return function (element) {
var value = element.checked ? element.value : '';
return __assign({
name: name,
value: value
}, validator(element, value));
};
}
/**
* Extract the data from a `<select>` element - here we can support single values
* or if the field is multiple it will return an array of values
* Create a data handler for select fields
* @param name
* @param el
* @param customValidators
* @param options
*/
function extractSelect(name, el, customValidators) {
function createSelectExtract(name, options) {
var validator = checkValidity(name, options);
/**

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

*/
function getMultiValue(collection) {

@@ -278,23 +311,25 @@ var value = [];

var value = el.multiple ? getMultiValue(el.selectedOptions) : el.value;
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
return function (element) {
var value = element.multiple ? getMultiValue(element.selectedOptions) : element.value;
return __assign({
name: name,
value: value
}, validator(element, value));
};
}
/**
* Extract data from the files
* Create a data handler for form fields
* @param name
* @param el
* @param customValidators
* @param options
*/
function extractFile(name, el, customValidators) {
var value = el.files;
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
function createFileExtract(name, options) {
var validator = checkValidity(name, options);
return function (element) {
var value = element.files;
return __assign({
name: name,
value: value
}, validator(element, value));
};
}

@@ -331,47 +366,50 @@

/**
* Create an event handler for the passed event and handle the value type
* @param name
* @param groupElements
* Creates an event handler for the passed element with it's data handler
* @param extractor
* @param stores
* @param customValidators
*/
function createEventHandler(name, groupElements, stores, customValidators) {
function createHandlerForData(extractor, stores) {
return function (event) {
var el = event.currentTarget || event.target;
valueUpdate(extractor(el), stores);
};
}
/**
* Create a handler for the passed element
* @param name
* @param eventName
* @param element
* @param groupElements
* @param stores
* @param options
*/
if (el instanceof HTMLSelectElement) {
valueUpdate(extractSelect(name, el, customValidators), stores);
} else {
switch (el.type) {
case 'checkbox':
{
valueUpdate(extractCheckbox(name, el, groupElements, customValidators), stores);
break;
}
case 'file':
{
valueUpdate(extractFile(name, el, customValidators), stores);
break;
}
function createHandler(name, eventName, element, groupElements, stores, options) {
var extract;
case 'radio':
{
valueUpdate(extractRadio(name, el), stores);
break;
}
if (element instanceof HTMLSelectElement) {
extract = createSelectExtract(name, options);
} else {
switch (element.type) {
case 'checkbox':
extract = createCheckboxExtract(name, groupElements, options);
break;
default:
{
valueUpdate(extractData(name, el, groupElements, customValidators), stores);
}
}
case 'radio':
extract = createRadioExtract(name, options);
break;
case 'file':
extract = createFileExtract(name, options);
break;
default:
extract = createFieldExtract(name, groupElements, options);
}
};
}
}
function createHandler(name, eventName, element, groupElements, stores, customValidators) {
var handler = createEventHandler(name, groupElements, stores, customValidators);
var handler = createHandlerForData(extract, stores);
element.addEventListener(eventName, handler);

@@ -420,56 +458,48 @@ return function () {

/**
* Create the initial value type for the current element, handling cases for multiple
* values
* Get the initial value from the passed elements
* @param name
* @param el
* @param groupElements
* @param elements
* @param stores
* @param customValidators
* @param options
*/
function createInitialValues(name, el, groupElements, stores, customValidators) {
if (el instanceof HTMLSelectElement) {
initValues(extractSelect(name, el, customValidators), stores);
} else {
switch (el.type) {
case 'checkbox':
{
initValues(extractCheckbox(name, el, groupElements, customValidators), stores);
break;
}
function getInitialValue(name, elements, stores, options) {
for (var i = 0; i < elements.length; i++) {
var el = elements[i];
var handler = void 0;
case 'file':
{
initValues(extractFile(name, el, customValidators), stores);
break;
}
if (el instanceof HTMLSelectElement) {
handler = createSelectExtract(name, options);
} else {
switch (el.type) {
case 'checkbox':
{
handler = createCheckboxExtract(name, elements, options);
break;
}
case 'radio':
{
initValues(extractRadio(name, el), stores);
break;
}
case 'file':
{
handler = createFileExtract(name, options);
break;
}
default:
{
initValues(extractData(name, el, groupElements, customValidators), stores);
}
case 'radio':
{
handler = createRadioExtract(name, options);
break;
}
default:
{
handler = createFieldExtract(name, elements, options);
}
}
}
initValues(handler(el), stores);
}
}
/**
* Get the initial value from the passed elements
* @param name
* @param elements
* @param stores
* @param validations
*/
function getInitialValue(name, elements, stores, validations) {
elements.forEach(function (el) {
return createInitialValues(name, el, elements, stores, validations);
});
}
/**
* Create the stores for the instance

@@ -529,3 +559,3 @@ */

return function () {
__spread(elMap).forEach(function (_a) {
return __spread(elMap).forEach(function (_a) {
var _b = __read(_a, 2),

@@ -594,2 +624,10 @@ el = _b[0],

});
__spread(handlers).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
handler = _b[1];
return el.removeEventListener('blur', handler);
});
}

@@ -606,3 +644,3 @@ })();

return function () {
__spread(handlers).forEach(function (_a) {
return __spread(handlers).forEach(function (_a) {
var _b = __read(_a, 2),

@@ -644,15 +682,12 @@ el = _b[0],

groupedMap.forEach(function (_a) {
var _b;
var _b = __read(_a, 2),
name = _b[0],
elements = _b[1];
var _c = __read(_a, 2),
name = _c[0],
elements = _c[1];
var customValidations = (_b = options === null || options === void 0 ? void 0 : options.validators) === null || _b === void 0 ? void 0 : _b[name];
touchHandlers.add(createTouchHandlers(name, elements, stores));
getInitialValue(name, elements, stores, customValidations);
getInitialValue(name, elements, stores, options);
dirtyHandlers.add(createDirtyHandler(name, elements, stores));
elements.forEach(function (el) {
if (el instanceof HTMLSelectElement) {
changeHandlers.add(createHandler(name, 'change', el, elements, stores, customValidations));
changeHandlers.add(createHandler(name, 'change', el, elements, stores, options));
} else {

@@ -669,3 +704,3 @@ switch (el.type) {

{
changeHandlers.add(createHandler(name, 'change', el, elements, stores, customValidations));
changeHandlers.add(createHandler(name, 'change', el, elements, stores, options));
break;

@@ -675,3 +710,3 @@ }

default:
keyupHandlers.add(createHandler(name, 'keyup', el, elements, stores, customValidations));
keyupHandlers.add(createHandler(name, 'keyup', el, elements, stores, options));
}

@@ -678,0 +713,0 @@ }

@@ -87,5 +87,6 @@ (function (global, factory) {

* @param el
* @param custom
*/
function extractErrors(el) {
function extractErrors(el, custom) {
var output = {};

@@ -102,3 +103,3 @@

return output;
return __assign(__assign({}, output), custom);
}

@@ -138,127 +139,158 @@ /**

}
function checkForCustomMessage(name, errors, el, options) {
var messages = options.messages;
var customMessages = messages && messages[name] || {};
var message;
var dataSet = el.dataset;
Object.keys(dataSet).forEach(function (key) {
if (errors[key]) {
message = dataSet[key];
}
});
if (!message) {
Object.keys(customMessages).forEach(function (key) {
if (errors[key]) {
message = customMessages[key];
}
});
}
return message;
}
/**
* Check the validity of a field and against custom validators
* @param el
* @param value
* @param customValidators
* @param name
* @param options
*/
function checkValidity(el, value, customValidators) {
var result = {
valid: el.checkValidity(),
message: el.validationMessage,
errors: extractErrors(el)
};
if ((value !== '' || value !== null) && customValidators) {
var validators = Object.entries(customValidators);
function checkValidity(name, options) {
/**
* Method called each time we want to do validity
*/
return function (el, value) {
el.setCustomValidity('');
for (var i = 0; i < validators.length; i++) {
var _a = __read(validators[i], 2),
name_2 = _a[0],
validator = _a[1];
if (!options) {
return {
valid: el.checkValidity(),
message: el.validationMessage,
errors: extractErrors(el)
};
}
var message = validator(value);
var validators = options.validators;
var customErrors = {}; // Handle custom validation
if (message === null) {
continue;
}
if ((value !== '' || value !== null) && validators && validators[name]) {
var customValidators = Object.entries(validators[name]);
if (result.valid === true) {
result.valid = false;
result.message = message;
result.errors[name_2] = true;
} else {
result.errors[name_2] = true;
for (var i = 0; i < customValidators.length; i++) {
var _a = __read(customValidators[i], 2),
name_2 = _a[0],
validator = _a[1];
var message_1 = validator(value);
if (message_1 !== null) {
if (!el.validationMessage) {
el.setCustomValidity(message_1);
}
customErrors[name_2] = true;
}
}
}
}
} // Check for any custom messages
return result;
var errors = extractErrors(el, customErrors);
var message = checkForCustomMessage(name, errors, el, options);
return {
valid: el.checkValidity(),
message: message || el.validationMessage,
errors: errors
};
};
}
/**
* Generic handle for extracting data from an `<input>` or `<textarea>` element that
* doesn't have a special case
* Create a data handler for any type of input field
* @param name
* @param element
* @param groupElements
* @param customValidators
* @param elementGroup
* @param options
*/
function extractData(name, element, groupElements, customValidators) {
var validValue = element.value === '' || typeof element.value === 'undefined' ? '' : element.value;
var value = groupElements.length > 1 ? groupElements.map(function (v) {
if (v.id === element.id) {
return validValue;
}
function createFieldExtract(name, elementGroup, options) {
var validator = checkValidity(name, options);
return function (element) {
var validValue = element.value === '' || element.value === null || typeof element.value === 'undefined' ? '' : element.value;
var value = elementGroup.length > 1 ? elementGroup.map(function (v) {
return v.id === element.id ? validValue : v.value;
}).filter(function (v) {
return v !== '';
}) : validValue; // Parse number values
return v.value;
}).filter(function (v) {
return v !== '';
}) : validValue;
if (['number', 'range'].includes(element.getAttribute('type'))) {
if (Array.isArray(value)) {
value = value.length > 0 ? value.map(function (v) {
return parseFloat(v);
}) : [];
} else {
value = value !== '' ? parseFloat(value) : null;
if (['number', 'range'].includes(element.getAttribute('type'))) {
if (Array.isArray(value)) {
value = value.length > 0 ? value.map(function (v) {
return parseFloat(v);
}) : [];
} else {
value = value !== '' ? parseFloat(value) : null;
}
}
}
var validity = checkValidity(element, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
return __assign({
name: name,
value: value
}, validator(element, value));
};
}
/**
* Extract the data from an `<input type="checkbox"> element - this returns a boolean value
* if a single checkbox. If multiple checkboxes are detected it returns an array value
* Create a data handler for checkbox fields
* @param name
* @param element The element being checked
* @param elements All elements from the name group
* @param customValidators
* @param elementGroup
* @param options
*/
function extractCheckbox(name, element, elements, customValidators) {
var value = elements.length > 1 ? elements.map(function (e) {
return e.id === element.id ? element.checked && element.value || null : e.checked && e.value || null;
}).filter(function (v) {
return v !== null;
}) : element.checked;
var validity = checkValidity(element, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
function createCheckboxExtract(name, elementGroup, options) {
var validator = checkValidity(name, options);
return function (element) {
var value = elementGroup.length > 1 ? elementGroup.map(function (e) {
return e.id === element.id ? element.checked && element.value || null : e.checked && e.value || null;
}).filter(function (v) {
return v !== null;
}) : element.checked;
return __assign({
name: name,
value: value
}, validator(element, value));
};
}
/**
* Extract the data from an `<input type="radio">` element, returning the value
* only for checked items, this works both with initial values and when the user
* selects a value
* Create a data handler for radio groups
* @param name
* @param el
* @param customValidators
* @param options
*/
function extractRadio(name, el, customValidators) {
var value = el.checked ? el.value : '';
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
function createRadioExtract(name, options) {
var validator = checkValidity(name, options);
return function (element) {
var value = element.checked ? element.value : '';
return __assign({
name: name,
value: value
}, validator(element, value));
};
}
/**
* Extract the data from a `<select>` element - here we can support single values
* or if the field is multiple it will return an array of values
* Create a data handler for select fields
* @param name
* @param el
* @param customValidators
* @param options
*/
function extractSelect(name, el, customValidators) {
function createSelectExtract(name, options) {
var validator = checkValidity(name, options);
/**

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

*/
function getMultiValue(collection) {

@@ -282,23 +315,25 @@ var value = [];

var value = el.multiple ? getMultiValue(el.selectedOptions) : el.value;
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
return function (element) {
var value = element.multiple ? getMultiValue(element.selectedOptions) : element.value;
return __assign({
name: name,
value: value
}, validator(element, value));
};
}
/**
* Extract data from the files
* Create a data handler for form fields
* @param name
* @param el
* @param customValidators
* @param options
*/
function extractFile(name, el, customValidators) {
var value = el.files;
var validity = checkValidity(el, value, customValidators);
return __assign({
name: name,
value: value
}, validity);
function createFileExtract(name, options) {
var validator = checkValidity(name, options);
return function (element) {
var value = element.files;
return __assign({
name: name,
value: value
}, validator(element, value));
};
}

@@ -335,47 +370,50 @@

/**
* Create an event handler for the passed event and handle the value type
* @param name
* @param groupElements
* Creates an event handler for the passed element with it's data handler
* @param extractor
* @param stores
* @param customValidators
*/
function createEventHandler(name, groupElements, stores, customValidators) {
function createHandlerForData(extractor, stores) {
return function (event) {
var el = event.currentTarget || event.target;
valueUpdate(extractor(el), stores);
};
}
/**
* Create a handler for the passed element
* @param name
* @param eventName
* @param element
* @param groupElements
* @param stores
* @param options
*/
if (el instanceof HTMLSelectElement) {
valueUpdate(extractSelect(name, el, customValidators), stores);
} else {
switch (el.type) {
case 'checkbox':
{
valueUpdate(extractCheckbox(name, el, groupElements, customValidators), stores);
break;
}
case 'file':
{
valueUpdate(extractFile(name, el, customValidators), stores);
break;
}
function createHandler(name, eventName, element, groupElements, stores, options) {
var extract;
case 'radio':
{
valueUpdate(extractRadio(name, el), stores);
break;
}
if (element instanceof HTMLSelectElement) {
extract = createSelectExtract(name, options);
} else {
switch (element.type) {
case 'checkbox':
extract = createCheckboxExtract(name, groupElements, options);
break;
default:
{
valueUpdate(extractData(name, el, groupElements, customValidators), stores);
}
}
case 'radio':
extract = createRadioExtract(name, options);
break;
case 'file':
extract = createFileExtract(name, options);
break;
default:
extract = createFieldExtract(name, groupElements, options);
}
};
}
}
function createHandler(name, eventName, element, groupElements, stores, customValidators) {
var handler = createEventHandler(name, groupElements, stores, customValidators);
var handler = createHandlerForData(extract, stores);
element.addEventListener(eventName, handler);

@@ -424,56 +462,48 @@ return function () {

/**
* Create the initial value type for the current element, handling cases for multiple
* values
* Get the initial value from the passed elements
* @param name
* @param el
* @param groupElements
* @param elements
* @param stores
* @param customValidators
* @param options
*/
function createInitialValues(name, el, groupElements, stores, customValidators) {
if (el instanceof HTMLSelectElement) {
initValues(extractSelect(name, el, customValidators), stores);
} else {
switch (el.type) {
case 'checkbox':
{
initValues(extractCheckbox(name, el, groupElements, customValidators), stores);
break;
}
function getInitialValue(name, elements, stores, options) {
for (var i = 0; i < elements.length; i++) {
var el = elements[i];
var handler = void 0;
case 'file':
{
initValues(extractFile(name, el, customValidators), stores);
break;
}
if (el instanceof HTMLSelectElement) {
handler = createSelectExtract(name, options);
} else {
switch (el.type) {
case 'checkbox':
{
handler = createCheckboxExtract(name, elements, options);
break;
}
case 'radio':
{
initValues(extractRadio(name, el), stores);
break;
}
case 'file':
{
handler = createFileExtract(name, options);
break;
}
default:
{
initValues(extractData(name, el, groupElements, customValidators), stores);
}
case 'radio':
{
handler = createRadioExtract(name, options);
break;
}
default:
{
handler = createFieldExtract(name, elements, options);
}
}
}
initValues(handler(el), stores);
}
}
/**
* Get the initial value from the passed elements
* @param name
* @param elements
* @param stores
* @param validations
*/
function getInitialValue(name, elements, stores, validations) {
elements.forEach(function (el) {
return createInitialValues(name, el, elements, stores, validations);
});
}
/**
* Create the stores for the instance

@@ -533,3 +563,3 @@ */

return function () {
__spread(elMap).forEach(function (_a) {
return __spread(elMap).forEach(function (_a) {
var _b = __read(_a, 2),

@@ -598,2 +628,10 @@ el = _b[0],

});
__spread(handlers).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
handler = _b[1];
return el.removeEventListener('blur', handler);
});
}

@@ -610,3 +648,3 @@ })();

return function () {
__spread(handlers).forEach(function (_a) {
return __spread(handlers).forEach(function (_a) {
var _b = __read(_a, 2),

@@ -648,15 +686,12 @@ el = _b[0],

groupedMap.forEach(function (_a) {
var _b;
var _b = __read(_a, 2),
name = _b[0],
elements = _b[1];
var _c = __read(_a, 2),
name = _c[0],
elements = _c[1];
var customValidations = (_b = options === null || options === void 0 ? void 0 : options.validators) === null || _b === void 0 ? void 0 : _b[name];
touchHandlers.add(createTouchHandlers(name, elements, stores));
getInitialValue(name, elements, stores, customValidations);
getInitialValue(name, elements, stores, options);
dirtyHandlers.add(createDirtyHandler(name, elements, stores));
elements.forEach(function (el) {
if (el instanceof HTMLSelectElement) {
changeHandlers.add(createHandler(name, 'change', el, elements, stores, customValidations));
changeHandlers.add(createHandler(name, 'change', el, elements, stores, options));
} else {

@@ -673,3 +708,3 @@ switch (el.type) {

{
changeHandlers.add(createHandler(name, 'change', el, elements, stores, customValidations));
changeHandlers.add(createHandler(name, 'change', el, elements, stores, options));
break;

@@ -679,3 +714,3 @@ }

default:
keyupHandlers.add(createHandler(name, 'keyup', el, elements, stores, customValidations));
keyupHandlers.add(createHandler(name, 'keyup', el, elements, stores, options));
}

@@ -682,0 +717,0 @@ }

@@ -11,7 +11,22 @@ /**

*/
export interface ExtractedFormInfo {
export interface FormulaField {
/**
* The name of the field being handled
*/
name?: string;
/**
* The current value or values of the field
*/
value: unknown | unknown[];
/**
* If the field is valid
*/
valid: boolean;
/**
* Any messages on the field from validation errors
*/
message: string;
/**
* Any errors from validations from HTML or custom validation
*/
errors: Record<string, boolean>;

@@ -18,0 +33,0 @@ }

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

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

@@ -7,13 +7,17 @@ * Optional settings for Formula

/**
* Override locale for sorting
* Locale for i18n - currently not used
*/
locale?: string;
/**
* Customised validity messages for each field error type
*/
messages?: CustomValidationMessages;
/**
* Custom Validators for fields
*/
validators?: CustomValidationRules;
validators?: ValidationRules;
/**
* Validation rules for the entire form
*/
formValidators?: ValidationRules;
formValidators?: ValidationRule;
}

@@ -8,6 +8,10 @@ /**

*/
export declare type ValidationRules = Record<string, ValidationFn>;
export declare type ValidationRule = Record<string, ValidationFn>;
/**
* Custom validation rules for Formula
*/
export declare type CustomValidationRules = Record<string, ValidationRules>;
export declare type ValidationRules = Record<string, ValidationRule>;
/**
* Custom validation messages for field errors
*/
export declare type CustomValidationMessages = Record<string, Record<string, string>>;
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