Socket
Socket
Sign inDemoInstall

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.3.0 to 0.4.0

types/formula.d.ts

6

CHANGELOG.md

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

## [0.4.0] 2021-02-16
### Changed
- Large Internal refactoring of the code
## [0.3.0] 2021-02-15

@@ -10,0 +16,0 @@

51

index.d.ts

@@ -1,5 +0,5 @@

import { Writable } from 'svelte/store';
import { FormErrors, FormValues } from './types/forms';
import { FormulaError, FormValues } from './types/forms';
import { FormulaOptions } from './types/options';
export { FormValues, FormErrors };
import { Formula } from './types/formula';
export { FormulaError, FormValues };
/**

@@ -12,45 +12,2 @@ * The `formula` function returns a form object that can be bound to any HTML

*/
export declare function formula(options?: FormulaOptions): {
/**
* The form action, this is used with Sveltes `use` directive which attaches to any element
* and handles internal form state creation
*/
form: (node: HTMLElement) => {
destroy: () => void;
};
/**
* The store with the current form values
* @typedef Writable<FormValues>
*/
formValues: Writable<Record<string, unknown>>;
/**
* The store with the form values at time of form submission
* @typedef Writable<FormValues>
*/
submitValues: Writable<Record<string, unknown>>;
/**
* 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
* @typedef Writable<Record<string, boolean>>
*/
touched: Writable<Record<string, boolean>>;
/**
* Store with the current dirty state of elements
* @typedef Writable<Record<string, boolean>>
*/
dirty: Writable<Record<string, boolean>>;
/**
* The store with the current form errors
* @typedef Writable<FormErrors>
*/
validity: Writable<Record<string, import("./types/forms").FormulaError>>;
/**
* Store containing the current overall validity state of the form
* @typedef Writable<boolean>
*/
isFormValid: Writable<boolean>;
};
export declare function formula(options?: FormulaOptions): Formula;

@@ -1,10 +0,11 @@

import { FormEl, FormValues } from '../types/forms';
import { Writable } from 'svelte/store';
import { FormEl } from '../types/forms';
import { FormulaStores } from '../types/formula';
/**
* Create a handler for an element for when it's focused, when it is called update the
* touched store and unsubscribe immediately
* @param el
* @param dirty
* @param values
* Create a set of dirty handlers for the current element group, set the value to false and store
* the initial value, listen for change events and when the dirty check is true, unsubscribe all
* event handlers
* @param name
* @param elements
* @param stores
*/
export declare function createDirtyHandler(el: FormEl, dirty: Writable<Record<string, boolean>>, values: Writable<FormValues>): void;
export declare function createDirtyHandler(name: string, elements: FormEl[], stores: FormulaStores): () => void;

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

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

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

* Do form level validations
* @param formValues
* @param formValidity
* @param isFormValid
* @param stores
* @param customValidators
*/
export declare function checkFormValidity(formValues: Writable<FormValues>, formValidity: Writable<Record<string, string>>, isFormValid: Writable<boolean>, customValidators: ValidationRules): () => void;
export declare function checkFormValidity(stores: FormulaStores, customValidators: ValidationRules): () => void;
/**

@@ -20,0 +18,0 @@ * Check the validity of a field and against custom validators

@@ -1,45 +0,10 @@

import { Writable } from 'svelte/store';
import { FormErrors } from '../types/forms';
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;
/**
* Create a generic value event handler
* @param values
* @param errors
* @param isValid
* @param updateMultiple
* @param customValidators
*/
export declare function createValueHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>, updateMultiple?: any, customValidators?: ValidationRules): (event: KeyboardEvent | MouseEvent) => void;
/**
* Create a handler for checkbox elements
* @param updateMultiple,
* @param values
* @param errors
* @param isValid
* @param customValidators
*/
export declare function createCheckHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>, updateMultiple?: any, customValidators?: ValidationRules): (event: KeyboardEvent | MouseEvent) => void;
/**
* Create a handler for radio elements
* @param values
* @param errors
* @param isValid
* @param customValidators
*/
export declare function createRadioHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>, customValidators?: ValidationRules): (event: KeyboardEvent | MouseEvent) => void;
/**
* Create a handler for select elements
* @param values
* @param errors
* @param isValid
* @param customValidators
*/
export declare function createSelectHandler(values: Writable<Record<string, unknown>>, errors: Writable<FormErrors>, isValid: Writable<boolean>, customValidators?: ValidationRules): (event: KeyboardEvent | MouseEvent) => void;
export declare function createFileHandler(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 formValues
* @param submitValues
* @param stores
*/
export declare function createSubmitHandler(formValues: Writable<Record<string, unknown>>, submitValues: Writable<Record<string, unknown>>): () => void;
export declare function createSubmitHandler(stores: FormulaStores): (event: Event) => void;

@@ -6,15 +6,17 @@ import { ExtractedFormInfo, FormEl } from '../types/forms';

* doesn't have a special case
* @param el
* @param updateMultiple
* @param name
* @param element
* @param groupElements
* @param customValidators
*/
export declare function extractData(el: FormEl, updateMultiple?: any, customValidators?: ValidationRules): ExtractedFormInfo;
export declare function extractData(name: string, element: FormEl, groupElements: FormEl[], customValidators?: ValidationRules): ExtractedFormInfo;
/**
* 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
* @param el
* @param updateMultiple
* @param name
* @param element The element being checked
* @param elements All elements from the name group
* @param customValidators
*/
export declare function extractCheckbox(el: HTMLInputElement, updateMultiple?: any, customValidators?: ValidationRules): ExtractedFormInfo;
export declare function extractCheckbox(name: string, element: HTMLInputElement, elements: HTMLInputElement[], customValidators?: ValidationRules): ExtractedFormInfo;
/**

@@ -24,18 +26,21 @@ * Extract the data from an `<input type="radio">` element, returning the value

* selects a value
* @param name
* @param el
* @param customValidators
*/
export declare function extractRadio(el: HTMLInputElement, customValidators?: ValidationRules): ExtractedFormInfo;
export declare function extractRadio(name: string, el: HTMLInputElement, customValidators?: ValidationRules): ExtractedFormInfo;
/**
* 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
* @param name
* @param el
* @param customValidators
*/
export declare function extractSelect(el: HTMLSelectElement, customValidators?: ValidationRules): ExtractedFormInfo;
export declare function extractSelect(name: string, el: HTMLSelectElement, customValidators?: ValidationRules): ExtractedFormInfo;
/**
* Extract data from the files
* @param name
* @param el
* @param customValidators
*/
export declare function extractFile(el: HTMLInputElement, customValidators?: ValidationRules): ExtractedFormInfo;
export declare function extractFile(name: string, el: HTMLInputElement, customValidators?: ValidationRules): ExtractedFormInfo;

@@ -1,12 +0,9 @@

import { FormErrors, FormValues } from '../types/forms';
import { Writable } from 'svelte/store';
import { FormulaOptions } from '../types/options';
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>>;
import { FormulaStores } from '../types/formula';
/**
* Creates the form action
* @param options
* @param stores
*/
export declare function createForm({ options, ...stores }: FormulaStores & {
options?: FormulaOptions;

@@ -13,0 +10,0 @@ }): (node: HTMLElement) => {

@@ -1,12 +0,25 @@

import { Writable } from 'svelte/store';
import { FormEl, FormErrors, FormValues } from '../types/forms';
import { FormEl } from '../types/forms';
import { FormulaStores } from '../types/formula';
import { ValidationFn } from '../types/validation';
/**
* Create the initial value type for the current element, handling cases for multiple
* values
* @param name
* @param el
* @param allElements
* @param values
* @param errors
* @param touched
* @param groupElements
* @param stores
* @param customValidators
*/
export declare function createInitialValues(el: FormEl, allElements: FormEl[], values: Writable<FormValues>, errors: Writable<FormErrors>, touched: Writable<Record<string, boolean>>): void;
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
* @param name
* @param elements
* @param stores
* @param validations
*/
export declare function getInitialValue(name: string, elements: FormEl[], stores: FormulaStores, validations: Record<string, ValidationFn>): void;
/**
* Create the stores for the instance
*/
export declare function createStores(): FormulaStores;
import { FormEl } from '../types/forms';
import { Writable } from 'svelte/store';
import { FormulaStores } from '../types/formula';
/**
* Create a handler for an element for when it's focused, when it is called update the
* touched store and unsubscribe immediately
* @param el
* @param touched
* Create a handler for adding and removing focus events on elements
* @param name
* @param elements
* @param stores
*/
export declare function createTouchHandler(el: FormEl, touched: Writable<Record<string, boolean>>): void;
export declare function createTouchHandlers(name: string, elements: FormEl[], stores: FormulaStores): () => void;
{
"name": "svelte-formula",
"description": "Reactive Forms for Svelte",
"version": "0.3.0",
"version": "0.4.0",
"keywords": [

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

@@ -29,2 +29,14 @@ import { writable } from 'svelte/store';

function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
function __read(o, n) {

@@ -67,26 +79,4 @@ var m = typeof Symbol === "function" && o[Symbol.iterator];

}
/**
* Check if our checkbox has multiple values of the same name
* @param name
* @param elements
*/
function isMultiCheckbox(name, elements) {
return elements.filter(function (el) {
return el.type === 'checkbox' && el.name === name;
}).length > 1;
}
/**
* Check if our checkbox has multiple values of the same name
* @param name
* @param elements
*/
function hasMultipleNames(name, elements) {
return elements.filter(function (el) {
return el.name === name;
}).length > 1;
}
/**
* Extract the errors from the element validity - as it's not enumerable, it cannot be

@@ -113,11 +103,9 @@ * destructured and we need to loop over the keys manually

* Do form level validations
* @param formValues
* @param formValidity
* @param isFormValid
* @param stores
* @param customValidators
*/
function checkFormValidity(formValues, formValidity, isFormValid, customValidators) {
return formValues.subscribe(function (values) {
formValidity.set({});
function checkFormValidity(stores, customValidators) {
return stores.formValues.subscribe(function (values) {
stores.formValidity.set({});
var validators = Object.entries(customValidators);

@@ -132,4 +120,4 @@

if (invalid) {
formValidity.update(function (state) {
if (invalid !== null) {
stores.formValidity.update(function (state) {
var _a;

@@ -139,3 +127,3 @@

});
isFormValid.set(false);
stores.isFormValid.set(false);
}

@@ -163,3 +151,3 @@ };

if (customValidators) {
if ((value !== '' || value !== null) && customValidators) {
var validators = Object.entries(customValidators);

@@ -194,19 +182,31 @@

* doesn't have a special case
* @param el
* @param updateMultiple
* @param name
* @param element
* @param groupElements
* @param customValidators
*/
function extractData(el, updateMultiple, customValidators) {
var name = el.getAttribute('name');
var val;
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;
}
if (['number', 'range'].includes(el.getAttribute('type'))) {
val = el.value === '' ? undefined : parseInt(el.value);
} else {
val = el.value;
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;
}
}
var value = updateMultiple ? updateMultiple(el.id, val) : val;
var validity = checkValidity(el, value, customValidators);
var validity = checkValidity(element, value, customValidators);
return __assign({

@@ -220,11 +220,15 @@ name: name,

* if a single checkbox. If multiple checkboxes are detected it returns an array value
* @param el
* @param updateMultiple
* @param name
* @param element The element being checked
* @param elements All elements from the name group
* @param customValidators
*/
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);
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({

@@ -239,2 +243,3 @@ name: name,

* selects a value
* @param name
* @param el

@@ -244,4 +249,3 @@ * @param customValidators

function extractRadio(el, customValidators) {
var name = el.getAttribute('name');
function extractRadio(name, el, customValidators) {
var value = el.checked ? el.value : '';

@@ -257,2 +261,3 @@ var validity = checkValidity(el, value, customValidators);

* or if the field is multiple it will return an array of values
* @param name
* @param el

@@ -262,3 +267,3 @@ * @param customValidators

function extractSelect(el, customValidators) {
function extractSelect(name, el, customValidators) {
/**

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

var name = el.getAttribute('name');
var value = el.multiple ? getMultiValue(el.selectedOptions) : el.value;

@@ -292,2 +296,3 @@ var validity = checkValidity(el, value, customValidators);

* Extract data from the files
* @param name
* @param el

@@ -297,4 +302,3 @@ * @param customValidators

function extractFile(el, customValidators) {
var name = el.getAttribute('name');
function extractFile(name, el, customValidators) {
var value = el.files;

@@ -311,9 +315,7 @@ var validity = checkValidity(el, value, customValidators);

* @param details
* @param values
* @param errors
* @param isValid
* @param stores
*/
function valueUpdate(details, values, errors, isValid) {
values.update(function (state) {
function valueUpdate(details, stores) {
stores.formValues.update(function (state) {
var _a;

@@ -323,3 +325,3 @@

});
errors.update(function (state) {
stores.validity.update(function (state) {
var _a;

@@ -334,3 +336,3 @@

isValid.set(Object.values(result).every(function (v) {
stores.isFormValid.set(Object.values(result).every(function (v) {
return v.valid;

@@ -342,7 +344,6 @@ }));

/**
* Create a generic value event handler
* @param values
* @param errors
* @param isValid
* @param updateMultiple
* Create an event handler for the passed event and handle the value type
* @param name
* @param groupElements
* @param stores
* @param customValidators

@@ -352,73 +353,54 @@ */

function createValueHandler(values, errors, isValid, updateMultiple, customValidators) {
function createEventHandler(name, groupElements, stores, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractData(el, updateMultiple, customValidators);
valueUpdate(details, values, errors, isValid);
};
}
/**
* Create a handler for checkbox elements
* @param updateMultiple,
* @param values
* @param errors
* @param isValid
* @param customValidators
*/
function createCheckHandler(values, errors, isValid, updateMultiple, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractCheckbox(el, updateMultiple, customValidators);
valueUpdate(details, values, errors, isValid);
};
}
/**
* Create a handler for radio elements
* @param values
* @param errors
* @param isValid
* @param customValidators
*/
if (el instanceof HTMLSelectElement) {
valueUpdate(extractSelect(name, el, customValidators), stores);
} else {
switch (el.type) {
case 'checkbox':
{
valueUpdate(extractCheckbox(name, el, groupElements, customValidators), stores);
break;
}
function createRadioHandler(values, errors, isValid, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractRadio(el, customValidators);
valueUpdate(details, values, errors, isValid);
case 'file':
{
valueUpdate(extractFile(name, el, customValidators), stores);
break;
}
case 'radio':
{
valueUpdate(extractRadio(name, el), stores);
break;
}
default:
{
valueUpdate(extractData(name, el, groupElements, customValidators), stores);
}
}
}
};
}
/**
* Create a handler for select elements
* @param values
* @param errors
* @param isValid
* @param customValidators
*/
function createSelectHandler(values, errors, isValid, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractSelect(el, customValidators);
valueUpdate(details, values, errors, isValid);
function createHandler(name, eventName, element, groupElements, stores, customValidators) {
var handler = createEventHandler(name, groupElements, stores, customValidators);
element.addEventListener(eventName, handler);
return function () {
element.removeEventListener(eventName, handler);
};
}
function createFileHandler(values, errors, isValid, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractFile(el, customValidators);
valueUpdate(details, values, errors, isValid);
};
}
/**
* 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 formValues
* @param submitValues
* @param stores
*/
function createSubmitHandler(formValues, submitValues) {
function createSubmitHandler(stores) {
return function () {
return formValues.subscribe(function (v) {
return submitValues.set(v);
return stores.formValues.subscribe(function (v) {
return stores.submitValues.set(v);
})();

@@ -428,77 +410,10 @@ };

var multiCheckbox = new Map();
var multiInput = new Map();
/**
* Function for handling updates to multiple checkbox support
* @private
* @param name
*/
function checkboxMultiUpdate(name) {
if (!multiCheckbox.has(name)) {
multiCheckbox.set(name, new Set());
}
/**
* Update the set store with the values for the multi checkbox
* @private
* @internal
*/
return function (checked, value) {
var set = multiCheckbox.get(name);
checked ? set.add(value) : set["delete"](value);
return __spread(set);
};
}
/**
* Support fields that have the same name value that are not checkboxes, these fields
* require a `id` property
* @param name
* @param locale
*/
function inputMultiUpdate(name, locale) {
if (!multiInput.has(name)) {
multiInput.set(name, new Map());
}
if (!locale && typeof navigator.language !== 'undefined') {
var lang = navigator.language.split('-');
locale = lang[0] || 'en'; // Always fall back to English for this;
}
return function (id, value) {
var map = multiInput.get(name);
map.set(id, value);
return __spread(map).sort(function (_a, _b) {
var _c = __read(_a, 1),
keyA = _c[0];
var _d = __read(_b, 1),
keyB = _d[0];
return keyA.localeCompare(keyB, locale, {
numeric: true
});
}).map(function (_a) {
var _b = __read(_a, 2),
_ = _b[0],
val = _b[1];
return val;
});
};
}
/**
* Initialise the value of the store with the details provided
* @param details
* @param values
* @param errors
* @param touched
* @param stores
*/
function initValues(details, values, errors, touched) {
values.update(function (state) {
function initValues(details, stores) {
stores.formValues.update(function (state) {
var _a;

@@ -508,3 +423,3 @@

});
errors.update(function (state) {
stores.validity.update(function (state) {
var _a;

@@ -519,7 +434,2 @@

});
touched.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[details.name] = false, _a));
});
}

@@ -529,189 +439,253 @@ /**

* values
* @param name
* @param el
* @param allElements
* @param values
* @param errors
* @param touched
* @param groupElements
* @param stores
* @param customValidators
*/
function createInitialValues(el, allElements, values, errors, touched) {
var details;
function createInitialValues(name, el, groupElements, stores, customValidators) {
if (el instanceof HTMLSelectElement) {
details = extractSelect(el);
} else if (el.type === 'radio') {
details = extractRadio(el);
} else if (el.type === 'checkbox') {
var name_1 = el.getAttribute('name');
var isMultiple = isMultiCheckbox(name_1, allElements);
var updateMultiple = void 0;
initValues(extractSelect(name, el, customValidators), stores);
} else {
switch (el.type) {
case 'checkbox':
{
initValues(extractCheckbox(name, el, groupElements, customValidators), stores);
break;
}
if (isMultiple) {
updateMultiple = checkboxMultiUpdate(name_1);
}
case 'file':
{
initValues(extractFile(name, el, customValidators), stores);
break;
}
details = extractCheckbox(el, updateMultiple);
} else {
var name_2 = el.getAttribute('name');
var isMultiple = hasMultipleNames(name_2, allElements);
var updateMultiple = void 0;
case 'radio':
{
initValues(extractRadio(name, el), stores);
break;
}
if (isMultiple) {
updateMultiple = inputMultiUpdate(name_2);
default:
{
initValues(extractData(name, el, groupElements, customValidators), stores);
}
}
details = extractData(el, updateMultiple);
}
}
/**
* Get the initial value from the passed elements
* @param name
* @param elements
* @param stores
* @param validations
*/
initValues(details, values, errors, touched);
function getInitialValue(name, elements, stores, validations) {
elements.forEach(function (el) {
return createInitialValues(name, el, elements, stores, validations);
});
}
/**
* Create the stores for the instance
*/
var hasTouched = new Set();
function createStores() {
return {
formValues: writable({}),
submitValues: writable({}),
touched: writable({}),
dirty: writable({}),
validity: writable({}),
formValidity: writable({}),
isFormValid: writable(false)
};
}
/**
* Create a handler for an element for when it's focused, when it is called update the
* touched store and unsubscribe immediately
* @param el
* @param touched
* Create a handler for adding and removing focus events on elements
* @param name
* @param elements
* @param stores
*/
function createTouchHandler(el, touched) {
var name = el.getAttribute('name');
/**
* Handle the update to the touched store then unsubscribe
* @private
* @param event
*/
function createTouchHandlers(name, elements, stores) {
var elMap = new Map();
stores.touched.update(function (state) {
var _a;
function updateTouched(event) {
touched.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[name] = false, _a));
});
return __assign(__assign({}, state), (_a = {}, _a[name] = true, _a));
});
el.removeEventListener('focus', updateTouched);
function updateTouched() {
return function () {
stores.touched.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[name] = true, _a));
});
__spread(elMap).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
handler = _b[1];
return el.removeEventListener('focus', handler);
});
};
}
if (!hasTouched.has(name)) {
el.addEventListener('focus', updateTouched);
hasTouched.add(name);
}
elements.forEach(function (el) {
var handler = updateTouched();
el.addEventListener('focus', handler);
elMap.set(el, handler);
});
return function () {
__spread(elMap).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
handler = _b[1];
return el.removeEventListener('focus', handler);
});
};
}
var hasDirty = new Map();
/**
* Create a handler for an element for when it's focused, when it is called update the
* touched store and unsubscribe immediately
* @param el
* @param dirty
* @param values
* Create a set of dirty handlers for the current element group, set the value to false and store
* the initial value, listen for change events and when the dirty check is true, unsubscribe all
* event handlers
* @param name
* @param elements
* @param stores
*/
function createDirtyHandler(el, dirty, values) {
var name = el.getAttribute('name');
/**
* Handle the update to the touched store then unsubscribe
* @private
* @param event
*/
function createDirtyHandler(name, elements, stores) {
var handlers = new Map();
var initialValue = new Map();
stores.dirty.update(function (state) {
var _a;
function updateDirty(event) {
var startValue = hasDirty.get(name);
values.subscribe(function (v) {
if (Array.isArray(v[name]) && v[name].length !== startValue.length) {
dirty.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[name] = false, _a));
});
stores.formValues.subscribe(function (v) {
return initialValue.set(name, v[name]);
})();
return __assign(__assign({}, state), (_a = {}, _a[name] = true, _a));
});
el.removeEventListener('blur', updateDirty);
} else if (v[name] !== startValue) {
dirty.update(function (state) {
var _a;
function updateDirty(groupName) {
return function () {
var startValue = initialValue.get(groupName);
stores.formValues.subscribe(function (v) {
if (Array.isArray(v[groupName])) {
var newVal = new Set(v[groupName]);
var existing_1 = new Set(startValue);
return __assign(__assign({}, state), (_a = {}, _a[name] = true, _a));
});
el.removeEventListener('blur', updateDirty);
}
})();
var same = __spread(newVal).every(function (e) {
return __spread(existing_1).includes(e);
});
if (!same) {
stores.dirty.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[groupName] = true, _a));
});
__spread(handlers).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
handler = _b[1];
return el.removeEventListener('blur', handler);
});
}
} else if (v[groupName] !== startValue) {
stores.dirty.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[groupName] = true, _a));
});
}
})();
};
}
if (!hasDirty.has(name)) {
values.subscribe(function (v) {
return hasDirty.set(name, v[name]);
})();
dirty.update(function (state) {
var _a;
elements.forEach(function (el) {
var handler = updateDirty(name);
el.addEventListener('blur', handler);
handlers.set(el, handler);
});
return function () {
__spread(handlers).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
handler = _b[1];
return __assign(__assign({}, state), (_a = {}, _a[name] = false, _a));
return el.removeEventListener('blur', handler);
});
el.addEventListener('blur', updateDirty);
}
};
}
/**
* Creates the form action
* @param options
* @param stores
*/
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;
var options = _a.options,
stores = __rest(_a, ["options"]);
return function form(node) {
var keyupHandlers = new Map();
var changeHandlers = new Map();
/**
* Store for all keyup handlers than need removed when destroyed
*/
var keyupHandlers = new Set();
var changeHandlers = new Set();
var touchHandlers = new Set();
var dirtyHandlers = new Set();
var submitHandler = undefined;
var formElements = getAllFieldsWithValidity(node);
formElements.forEach(function (el) {
var _a; // Create a single touch handler for each element, this is removed after it has first been focused
var groupedMap = __spread(formElements.reduce(function (entryMap, e) {
var name = e.getAttribute('name');
return entryMap.set(name, __spread(entryMap.get(name) || [], [e]));
}, new Map()));
createTouchHandler(el, touched);
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];
groupedMap.forEach(function (_a) {
var _b;
if (el instanceof HTMLSelectElement) {
var handler = createSelectHandler(formValues, validity, isFormValid, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else if (el.type === 'radio') {
var handler = createRadioHandler(formValues, validity, isFormValid, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else if (el.type === 'checkbox') {
var isMultiple = isMultiCheckbox(name, formElements);
var updateMultiple = void 0;
var _c = __read(_a, 2),
name = _c[0],
elements = _c[1];
if (isMultiple) {
updateMultiple = checkboxMultiUpdate(name);
}
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);
dirtyHandlers.add(createDirtyHandler(name, elements, stores));
elements.forEach(function (el) {
if (el instanceof HTMLSelectElement) {
changeHandlers.add(createHandler(name, 'change', el, elements, stores, customValidations));
} else {
switch (el.type) {
case 'radio':
case 'checkbox':
case 'file':
case 'range':
case 'color':
case 'date':
case 'time':
case 'week':
{
changeHandlers.add(createHandler(name, 'change', el, elements, stores, customValidations));
break;
}
var handler = createCheckHandler(formValues, validity, isFormValid, updateMultiple, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else if (el.type === 'file') {
var handler = createFileHandler(formValues, validity, isFormValid, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else {
var isMultiple = hasMultipleNames(name, formElements);
var updateMultiple = void 0;
if (isMultiple) {
updateMultiple = inputMultiUpdate(name, options === null || options === void 0 ? void 0 : options.locale);
default:
keyupHandlers.add(createHandler(name, 'keyup', el, elements, stores, customValidations));
}
}
var handler = createValueHandler(formValues, validity, isFormValid, updateMultiple, customValidations);
if (['range', 'color', 'date', 'time', 'week'].includes(el.type)) {
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else {
el.addEventListener('keyup', handler);
keyupHandlers.set(el, handler);
}
}
});
});

@@ -722,7 +696,7 @@

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

@@ -735,18 +709,6 @@ }

__spread(keyupHandlers).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
fn = _b[1];
el.removeEventListener('keyup', fn);
__spread(keyupHandlers, changeHandlers, touchHandlers, dirtyHandlers).forEach(function (fn) {
return fn();
});
__spread(changeHandlers).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
fn = _b[1];
el.removeEventListener('change', fn);
});
if (submitHandler) {

@@ -769,69 +731,10 @@ node.removeEventListener('submit', submitHandler);

function formula(options) {
var formValues = writable({});
var submitValues = writable({});
var touched = writable({});
var dirty = writable({});
var validity = writable({});
var formValidity = writable({});
var isFormValid = writable(false);
return {
/**
* The form action, this is used with Sveltes `use` directive which attaches to any element
* and handles internal form state creation
*/
form: createForm({
formValues: formValues,
submitValues: submitValues,
formValidity: formValidity,
validity: validity,
isFormValid: isFormValid,
touched: touched,
dirty: dirty,
var stores = createStores();
return __assign({
form: createForm(__assign(__assign({}, stores), {
options: options
}),
/**
* The store with the current form values
* @typedef Writable<FormValues>
*/
formValues: formValues,
/**
* The store with the form values at time of form submission
* @typedef Writable<FormValues>
*/
submitValues: submitValues,
/**
* 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
* @typedef Writable<Record<string, boolean>>
*/
touched: touched,
/**
* Store with the current dirty state of elements
* @typedef Writable<Record<string, boolean>>
*/
dirty: dirty,
/**
* The store with the current form errors
* @typedef Writable<FormErrors>
*/
validity: validity,
/**
* Store containing the current overall validity state of the form
* @typedef Writable<boolean>
*/
isFormValid: isFormValid
};
}))
}, stores);
}
export { formula };

@@ -33,2 +33,14 @@ (function (global, factory) {

function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
function __read(o, n) {

@@ -71,26 +83,4 @@ var m = typeof Symbol === "function" && o[Symbol.iterator];

}
/**
* Check if our checkbox has multiple values of the same name
* @param name
* @param elements
*/
function isMultiCheckbox(name, elements) {
return elements.filter(function (el) {
return el.type === 'checkbox' && el.name === name;
}).length > 1;
}
/**
* Check if our checkbox has multiple values of the same name
* @param name
* @param elements
*/
function hasMultipleNames(name, elements) {
return elements.filter(function (el) {
return el.name === name;
}).length > 1;
}
/**
* Extract the errors from the element validity - as it's not enumerable, it cannot be

@@ -117,11 +107,9 @@ * destructured and we need to loop over the keys manually

* Do form level validations
* @param formValues
* @param formValidity
* @param isFormValid
* @param stores
* @param customValidators
*/
function checkFormValidity(formValues, formValidity, isFormValid, customValidators) {
return formValues.subscribe(function (values) {
formValidity.set({});
function checkFormValidity(stores, customValidators) {
return stores.formValues.subscribe(function (values) {
stores.formValidity.set({});
var validators = Object.entries(customValidators);

@@ -136,4 +124,4 @@

if (invalid) {
formValidity.update(function (state) {
if (invalid !== null) {
stores.formValidity.update(function (state) {
var _a;

@@ -143,3 +131,3 @@

});
isFormValid.set(false);
stores.isFormValid.set(false);
}

@@ -167,3 +155,3 @@ };

if (customValidators) {
if ((value !== '' || value !== null) && customValidators) {
var validators = Object.entries(customValidators);

@@ -198,19 +186,31 @@

* doesn't have a special case
* @param el
* @param updateMultiple
* @param name
* @param element
* @param groupElements
* @param customValidators
*/
function extractData(el, updateMultiple, customValidators) {
var name = el.getAttribute('name');
var val;
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;
}
if (['number', 'range'].includes(el.getAttribute('type'))) {
val = el.value === '' ? undefined : parseInt(el.value);
} else {
val = el.value;
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;
}
}
var value = updateMultiple ? updateMultiple(el.id, val) : val;
var validity = checkValidity(el, value, customValidators);
var validity = checkValidity(element, value, customValidators);
return __assign({

@@ -224,11 +224,15 @@ name: name,

* if a single checkbox. If multiple checkboxes are detected it returns an array value
* @param el
* @param updateMultiple
* @param name
* @param element The element being checked
* @param elements All elements from the name group
* @param customValidators
*/
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);
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({

@@ -243,2 +247,3 @@ name: name,

* selects a value
* @param name
* @param el

@@ -248,4 +253,3 @@ * @param customValidators

function extractRadio(el, customValidators) {
var name = el.getAttribute('name');
function extractRadio(name, el, customValidators) {
var value = el.checked ? el.value : '';

@@ -261,2 +265,3 @@ var validity = checkValidity(el, value, customValidators);

* or if the field is multiple it will return an array of values
* @param name
* @param el

@@ -266,3 +271,3 @@ * @param customValidators

function extractSelect(el, customValidators) {
function extractSelect(name, el, customValidators) {
/**

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

var name = el.getAttribute('name');
var value = el.multiple ? getMultiValue(el.selectedOptions) : el.value;

@@ -296,2 +300,3 @@ var validity = checkValidity(el, value, customValidators);

* Extract data from the files
* @param name
* @param el

@@ -301,4 +306,3 @@ * @param customValidators

function extractFile(el, customValidators) {
var name = el.getAttribute('name');
function extractFile(name, el, customValidators) {
var value = el.files;

@@ -315,9 +319,7 @@ var validity = checkValidity(el, value, customValidators);

* @param details
* @param values
* @param errors
* @param isValid
* @param stores
*/
function valueUpdate(details, values, errors, isValid) {
values.update(function (state) {
function valueUpdate(details, stores) {
stores.formValues.update(function (state) {
var _a;

@@ -327,3 +329,3 @@

});
errors.update(function (state) {
stores.validity.update(function (state) {
var _a;

@@ -338,3 +340,3 @@

isValid.set(Object.values(result).every(function (v) {
stores.isFormValid.set(Object.values(result).every(function (v) {
return v.valid;

@@ -346,7 +348,6 @@ }));

/**
* Create a generic value event handler
* @param values
* @param errors
* @param isValid
* @param updateMultiple
* Create an event handler for the passed event and handle the value type
* @param name
* @param groupElements
* @param stores
* @param customValidators

@@ -356,73 +357,54 @@ */

function createValueHandler(values, errors, isValid, updateMultiple, customValidators) {
function createEventHandler(name, groupElements, stores, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractData(el, updateMultiple, customValidators);
valueUpdate(details, values, errors, isValid);
};
}
/**
* Create a handler for checkbox elements
* @param updateMultiple,
* @param values
* @param errors
* @param isValid
* @param customValidators
*/
function createCheckHandler(values, errors, isValid, updateMultiple, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractCheckbox(el, updateMultiple, customValidators);
valueUpdate(details, values, errors, isValid);
};
}
/**
* Create a handler for radio elements
* @param values
* @param errors
* @param isValid
* @param customValidators
*/
if (el instanceof HTMLSelectElement) {
valueUpdate(extractSelect(name, el, customValidators), stores);
} else {
switch (el.type) {
case 'checkbox':
{
valueUpdate(extractCheckbox(name, el, groupElements, customValidators), stores);
break;
}
function createRadioHandler(values, errors, isValid, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractRadio(el, customValidators);
valueUpdate(details, values, errors, isValid);
case 'file':
{
valueUpdate(extractFile(name, el, customValidators), stores);
break;
}
case 'radio':
{
valueUpdate(extractRadio(name, el), stores);
break;
}
default:
{
valueUpdate(extractData(name, el, groupElements, customValidators), stores);
}
}
}
};
}
/**
* Create a handler for select elements
* @param values
* @param errors
* @param isValid
* @param customValidators
*/
function createSelectHandler(values, errors, isValid, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractSelect(el, customValidators);
valueUpdate(details, values, errors, isValid);
function createHandler(name, eventName, element, groupElements, stores, customValidators) {
var handler = createEventHandler(name, groupElements, stores, customValidators);
element.addEventListener(eventName, handler);
return function () {
element.removeEventListener(eventName, handler);
};
}
function createFileHandler(values, errors, isValid, customValidators) {
return function (event) {
var el = event.currentTarget || event.target;
var details = extractFile(el, customValidators);
valueUpdate(details, values, errors, isValid);
};
}
/**
* 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 formValues
* @param submitValues
* @param stores
*/
function createSubmitHandler(formValues, submitValues) {
function createSubmitHandler(stores) {
return function () {
return formValues.subscribe(function (v) {
return submitValues.set(v);
return stores.formValues.subscribe(function (v) {
return stores.submitValues.set(v);
})();

@@ -432,77 +414,10 @@ };

var multiCheckbox = new Map();
var multiInput = new Map();
/**
* Function for handling updates to multiple checkbox support
* @private
* @param name
*/
function checkboxMultiUpdate(name) {
if (!multiCheckbox.has(name)) {
multiCheckbox.set(name, new Set());
}
/**
* Update the set store with the values for the multi checkbox
* @private
* @internal
*/
return function (checked, value) {
var set = multiCheckbox.get(name);
checked ? set.add(value) : set["delete"](value);
return __spread(set);
};
}
/**
* Support fields that have the same name value that are not checkboxes, these fields
* require a `id` property
* @param name
* @param locale
*/
function inputMultiUpdate(name, locale) {
if (!multiInput.has(name)) {
multiInput.set(name, new Map());
}
if (!locale && typeof navigator.language !== 'undefined') {
var lang = navigator.language.split('-');
locale = lang[0] || 'en'; // Always fall back to English for this;
}
return function (id, value) {
var map = multiInput.get(name);
map.set(id, value);
return __spread(map).sort(function (_a, _b) {
var _c = __read(_a, 1),
keyA = _c[0];
var _d = __read(_b, 1),
keyB = _d[0];
return keyA.localeCompare(keyB, locale, {
numeric: true
});
}).map(function (_a) {
var _b = __read(_a, 2),
_ = _b[0],
val = _b[1];
return val;
});
};
}
/**
* Initialise the value of the store with the details provided
* @param details
* @param values
* @param errors
* @param touched
* @param stores
*/
function initValues(details, values, errors, touched) {
values.update(function (state) {
function initValues(details, stores) {
stores.formValues.update(function (state) {
var _a;

@@ -512,3 +427,3 @@

});
errors.update(function (state) {
stores.validity.update(function (state) {
var _a;

@@ -523,7 +438,2 @@

});
touched.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[details.name] = false, _a));
});
}

@@ -533,189 +443,253 @@ /**

* values
* @param name
* @param el
* @param allElements
* @param values
* @param errors
* @param touched
* @param groupElements
* @param stores
* @param customValidators
*/
function createInitialValues(el, allElements, values, errors, touched) {
var details;
function createInitialValues(name, el, groupElements, stores, customValidators) {
if (el instanceof HTMLSelectElement) {
details = extractSelect(el);
} else if (el.type === 'radio') {
details = extractRadio(el);
} else if (el.type === 'checkbox') {
var name_1 = el.getAttribute('name');
var isMultiple = isMultiCheckbox(name_1, allElements);
var updateMultiple = void 0;
initValues(extractSelect(name, el, customValidators), stores);
} else {
switch (el.type) {
case 'checkbox':
{
initValues(extractCheckbox(name, el, groupElements, customValidators), stores);
break;
}
if (isMultiple) {
updateMultiple = checkboxMultiUpdate(name_1);
}
case 'file':
{
initValues(extractFile(name, el, customValidators), stores);
break;
}
details = extractCheckbox(el, updateMultiple);
} else {
var name_2 = el.getAttribute('name');
var isMultiple = hasMultipleNames(name_2, allElements);
var updateMultiple = void 0;
case 'radio':
{
initValues(extractRadio(name, el), stores);
break;
}
if (isMultiple) {
updateMultiple = inputMultiUpdate(name_2);
default:
{
initValues(extractData(name, el, groupElements, customValidators), stores);
}
}
details = extractData(el, updateMultiple);
}
}
/**
* Get the initial value from the passed elements
* @param name
* @param elements
* @param stores
* @param validations
*/
initValues(details, values, errors, touched);
function getInitialValue(name, elements, stores, validations) {
elements.forEach(function (el) {
return createInitialValues(name, el, elements, stores, validations);
});
}
/**
* Create the stores for the instance
*/
var hasTouched = new Set();
function createStores() {
return {
formValues: store.writable({}),
submitValues: store.writable({}),
touched: store.writable({}),
dirty: store.writable({}),
validity: store.writable({}),
formValidity: store.writable({}),
isFormValid: store.writable(false)
};
}
/**
* Create a handler for an element for when it's focused, when it is called update the
* touched store and unsubscribe immediately
* @param el
* @param touched
* Create a handler for adding and removing focus events on elements
* @param name
* @param elements
* @param stores
*/
function createTouchHandler(el, touched) {
var name = el.getAttribute('name');
/**
* Handle the update to the touched store then unsubscribe
* @private
* @param event
*/
function createTouchHandlers(name, elements, stores) {
var elMap = new Map();
stores.touched.update(function (state) {
var _a;
function updateTouched(event) {
touched.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[name] = false, _a));
});
return __assign(__assign({}, state), (_a = {}, _a[name] = true, _a));
});
el.removeEventListener('focus', updateTouched);
function updateTouched() {
return function () {
stores.touched.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[name] = true, _a));
});
__spread(elMap).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
handler = _b[1];
return el.removeEventListener('focus', handler);
});
};
}
if (!hasTouched.has(name)) {
el.addEventListener('focus', updateTouched);
hasTouched.add(name);
}
elements.forEach(function (el) {
var handler = updateTouched();
el.addEventListener('focus', handler);
elMap.set(el, handler);
});
return function () {
__spread(elMap).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
handler = _b[1];
return el.removeEventListener('focus', handler);
});
};
}
var hasDirty = new Map();
/**
* Create a handler for an element for when it's focused, when it is called update the
* touched store and unsubscribe immediately
* @param el
* @param dirty
* @param values
* Create a set of dirty handlers for the current element group, set the value to false and store
* the initial value, listen for change events and when the dirty check is true, unsubscribe all
* event handlers
* @param name
* @param elements
* @param stores
*/
function createDirtyHandler(el, dirty, values) {
var name = el.getAttribute('name');
/**
* Handle the update to the touched store then unsubscribe
* @private
* @param event
*/
function createDirtyHandler(name, elements, stores) {
var handlers = new Map();
var initialValue = new Map();
stores.dirty.update(function (state) {
var _a;
function updateDirty(event) {
var startValue = hasDirty.get(name);
values.subscribe(function (v) {
if (Array.isArray(v[name]) && v[name].length !== startValue.length) {
dirty.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[name] = false, _a));
});
stores.formValues.subscribe(function (v) {
return initialValue.set(name, v[name]);
})();
return __assign(__assign({}, state), (_a = {}, _a[name] = true, _a));
});
el.removeEventListener('blur', updateDirty);
} else if (v[name] !== startValue) {
dirty.update(function (state) {
var _a;
function updateDirty(groupName) {
return function () {
var startValue = initialValue.get(groupName);
stores.formValues.subscribe(function (v) {
if (Array.isArray(v[groupName])) {
var newVal = new Set(v[groupName]);
var existing_1 = new Set(startValue);
return __assign(__assign({}, state), (_a = {}, _a[name] = true, _a));
});
el.removeEventListener('blur', updateDirty);
}
})();
var same = __spread(newVal).every(function (e) {
return __spread(existing_1).includes(e);
});
if (!same) {
stores.dirty.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[groupName] = true, _a));
});
__spread(handlers).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
handler = _b[1];
return el.removeEventListener('blur', handler);
});
}
} else if (v[groupName] !== startValue) {
stores.dirty.update(function (state) {
var _a;
return __assign(__assign({}, state), (_a = {}, _a[groupName] = true, _a));
});
}
})();
};
}
if (!hasDirty.has(name)) {
values.subscribe(function (v) {
return hasDirty.set(name, v[name]);
})();
dirty.update(function (state) {
var _a;
elements.forEach(function (el) {
var handler = updateDirty(name);
el.addEventListener('blur', handler);
handlers.set(el, handler);
});
return function () {
__spread(handlers).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
handler = _b[1];
return __assign(__assign({}, state), (_a = {}, _a[name] = false, _a));
return el.removeEventListener('blur', handler);
});
el.addEventListener('blur', updateDirty);
}
};
}
/**
* Creates the form action
* @param options
* @param stores
*/
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;
var options = _a.options,
stores = __rest(_a, ["options"]);
return function form(node) {
var keyupHandlers = new Map();
var changeHandlers = new Map();
/**
* Store for all keyup handlers than need removed when destroyed
*/
var keyupHandlers = new Set();
var changeHandlers = new Set();
var touchHandlers = new Set();
var dirtyHandlers = new Set();
var submitHandler = undefined;
var formElements = getAllFieldsWithValidity(node);
formElements.forEach(function (el) {
var _a; // Create a single touch handler for each element, this is removed after it has first been focused
var groupedMap = __spread(formElements.reduce(function (entryMap, e) {
var name = e.getAttribute('name');
return entryMap.set(name, __spread(entryMap.get(name) || [], [e]));
}, new Map()));
createTouchHandler(el, touched);
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];
groupedMap.forEach(function (_a) {
var _b;
if (el instanceof HTMLSelectElement) {
var handler = createSelectHandler(formValues, validity, isFormValid, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else if (el.type === 'radio') {
var handler = createRadioHandler(formValues, validity, isFormValid, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else if (el.type === 'checkbox') {
var isMultiple = isMultiCheckbox(name, formElements);
var updateMultiple = void 0;
var _c = __read(_a, 2),
name = _c[0],
elements = _c[1];
if (isMultiple) {
updateMultiple = checkboxMultiUpdate(name);
}
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);
dirtyHandlers.add(createDirtyHandler(name, elements, stores));
elements.forEach(function (el) {
if (el instanceof HTMLSelectElement) {
changeHandlers.add(createHandler(name, 'change', el, elements, stores, customValidations));
} else {
switch (el.type) {
case 'radio':
case 'checkbox':
case 'file':
case 'range':
case 'color':
case 'date':
case 'time':
case 'week':
{
changeHandlers.add(createHandler(name, 'change', el, elements, stores, customValidations));
break;
}
var handler = createCheckHandler(formValues, validity, isFormValid, updateMultiple, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else if (el.type === 'file') {
var handler = createFileHandler(formValues, validity, isFormValid, customValidations);
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else {
var isMultiple = hasMultipleNames(name, formElements);
var updateMultiple = void 0;
if (isMultiple) {
updateMultiple = inputMultiUpdate(name, options === null || options === void 0 ? void 0 : options.locale);
default:
keyupHandlers.add(createHandler(name, 'keyup', el, elements, stores, customValidations));
}
}
var handler = createValueHandler(formValues, validity, isFormValid, updateMultiple, customValidations);
if (['range', 'color', 'date', 'time', 'week'].includes(el.type)) {
el.addEventListener('change', handler);
changeHandlers.set(el, handler);
} else {
el.addEventListener('keyup', handler);
keyupHandlers.set(el, handler);
}
}
});
});

@@ -726,7 +700,7 @@

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

@@ -739,18 +713,6 @@ }

__spread(keyupHandlers).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
fn = _b[1];
el.removeEventListener('keyup', fn);
__spread(keyupHandlers, changeHandlers, touchHandlers, dirtyHandlers).forEach(function (fn) {
return fn();
});
__spread(changeHandlers).forEach(function (_a) {
var _b = __read(_a, 2),
el = _b[0],
fn = _b[1];
el.removeEventListener('change', fn);
});
if (submitHandler) {

@@ -773,67 +735,8 @@ node.removeEventListener('submit', submitHandler);

function formula(options) {
var formValues = store.writable({});
var submitValues = store.writable({});
var touched = store.writable({});
var dirty = store.writable({});
var validity = store.writable({});
var formValidity = store.writable({});
var isFormValid = store.writable(false);
return {
/**
* The form action, this is used with Sveltes `use` directive which attaches to any element
* and handles internal form state creation
*/
form: createForm({
formValues: formValues,
submitValues: submitValues,
formValidity: formValidity,
validity: validity,
isFormValid: isFormValid,
touched: touched,
dirty: dirty,
var stores = createStores();
return __assign({
form: createForm(__assign(__assign({}, stores), {
options: options
}),
/**
* The store with the current form values
* @typedef Writable<FormValues>
*/
formValues: formValues,
/**
* The store with the form values at time of form submission
* @typedef Writable<FormValues>
*/
submitValues: submitValues,
/**
* 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
* @typedef Writable<Record<string, boolean>>
*/
touched: touched,
/**
* Store with the current dirty state of elements
* @typedef Writable<Record<string, boolean>>
*/
dirty: dirty,
/**
* The store with the current form errors
* @typedef Writable<FormErrors>
*/
validity: validity,
/**
* Store containing the current overall validity state of the form
* @typedef Writable<boolean>
*/
isFormValid: isFormValid
};
}))
}, stores);
}

@@ -840,0 +743,0 @@

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

/**
* Possible values that can exist on out data
*/
declare type ValueType = unknown | unknown[];
/**
* Internally extracted data from the form element, used to generate other store values

@@ -17,4 +13,4 @@ * @internal

export interface ExtractedFormInfo {
name: string;
value: ValueType;
name?: string;
value: unknown | unknown[];
valid: boolean;

@@ -48,3 +44,3 @@ message: string;

*/
export declare type FormValues = Record<string, ValueType>;
export declare type FormValues = Record<string, unknown | unknown[]>;
/**

@@ -54,2 +50,1 @@ * Form Errors

export declare type FormErrors = Record<string, FormulaError>;
export {};
/**
* A validation function, it should return null if there is no error, or a string if there is an error
*/
export declare type ValidationFn = (value: unknown | unknown[]) => null | string;
export declare type ValidationFn = (value: unknown | unknown[]) => string | null;
/**

@@ -6,0 +6,0 @@ * A single validation rule with the name of the rule and validation function

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