svelte-formula
Advanced tools
Comparing version 0.3.0 to 0.4.0
@@ -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 @@ |
@@ -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 |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
92401
1528