svelte-formula
Advanced tools
Comparing version 0.9.1 to 0.9.2
@@ -8,2 +8,15 @@ # Changelog | ||
## [0.9.2] - 2021-03-09 | ||
### Changed | ||
- Removed Beaker changes for `MutationObserver` - reverted code back to using a check for all rows and re-create forms | ||
each time as tracking changes becomes too complex. New code in place that handles subscription correctly to child | ||
stores for each form instance within the group and better extraction of values. This also fixes infinite loop bug | ||
caused by new code. | ||
### Fixed | ||
- Beaker stores now correctly showing store values in a group without requiring `bind:value` | ||
## [0.9.1] - 2021-03-08 | ||
@@ -24,3 +37,3 @@ | ||
- Beaker now accepts `defaultValues` as an array (`group.init` will always overide this) | ||
- Beaker now accepts `defaultValues` as an array (`group.init` will always override this) | ||
- Internal refactoring to improve group handling | ||
@@ -27,0 +40,0 @@ - Removed global state stores for initial values, now only generated internally for reset methods |
@@ -20,2 +20,2 @@ import { FormEl, FormulaError, ValidationRule, FormulaStores, FormulaOptions } from '../../types'; | ||
*/ | ||
export declare function createValidationChecker(inputGroup: string, options?: FormulaOptions): (el: FormEl, elValue: unknown | unknown[]) => FormulaError; | ||
export declare function createValidationChecker(inputGroup: string, elementGroup: FormEl[], options?: FormulaOptions): (el: FormEl, elValue: unknown | unknown[]) => FormulaError; |
@@ -24,2 +24,2 @@ import { FormEl, FormulaField, FormulaOptions, FormulaStores } from '../../types'; | ||
*/ | ||
export declare function createSubmitHandler<T extends Record<string, unknown | unknown[]>>(stores: FormulaStores<T>): (event: Event) => void; | ||
export declare function createSubmitHandler<T extends Record<string, unknown | unknown[]>>(stores: FormulaStores<T>, form: HTMLFormElement): (event: Event) => void; |
@@ -7,3 +7,4 @@ import { Formula, FormulaOptions, FormulaStores } from '../../types'; | ||
* @param groupName | ||
* @param initialData | ||
*/ | ||
export declare function createForm<T extends Record<string, unknown | unknown[]>>(options: FormulaOptions, globalStore?: Map<string, FormulaStores<T>>, groupName?: string): Formula<T>; | ||
export declare function createForm<T extends Record<string, unknown | unknown[]>>(options: FormulaOptions, globalStore?: Map<string, FormulaStores<T>>, groupName?: string, initialData?: T): Formula<T>; |
{ | ||
"name": "svelte-formula", | ||
"description": "Reactive Forms for Svelte", | ||
"version": "0.9.1", | ||
"version": "0.9.2", | ||
"keywords": [ | ||
@@ -6,0 +6,0 @@ "svelte", |
@@ -204,3 +204,3 @@ import { get, writable } from 'svelte/store'; | ||
function createValidationChecker(inputGroup, options) { | ||
function createValidationChecker(inputGroup, elementGroup, options) { | ||
/** | ||
@@ -220,4 +220,6 @@ * Method called each time a field is updated | ||
el.setCustomValidity(''); | ||
el.removeAttribute('data-formula-invalid'); // If there's no options, just return the current error | ||
elementGroup.forEach(function (gel) { | ||
gel.setCustomValidity(''); | ||
gel.removeAttribute('data-formula-invalid'); | ||
}); // If there's no options, just return the current error | ||
@@ -508,3 +510,3 @@ if (!options) { | ||
function createFieldExtract(name, elementGroup, options, stores) { | ||
var validator = createValidationChecker(name, options); | ||
var validator = createValidationChecker(name, elementGroup, options); | ||
@@ -539,4 +541,8 @@ var isMultiValue = function () { | ||
if (isInit && (isMultiValue || element.type === 'select-multiple')) { | ||
value = elValue.length === 0 ? value : elValue; | ||
if (isInit) { | ||
if (isMultiValue || element.type === 'select-multiple') { | ||
value = elValue.length === 0 ? value : elValue; | ||
} else if (typeof value === undefined) { | ||
value = elValue === null ? '' : elValue; | ||
} | ||
} else { | ||
@@ -680,5 +686,6 @@ value = elValue === null ? '' : elValue; | ||
function createSubmitHandler(stores) { | ||
function createSubmitHandler(stores, form) { | ||
return function () { | ||
return stores.formValues.subscribe(function (v) { | ||
form.reportValidity(); | ||
stores.formValues.subscribe(function (v) { | ||
return stores.submitValues.set(v); | ||
@@ -1174,5 +1181,6 @@ })(); | ||
* @param groupName | ||
* @param initialData | ||
*/ | ||
function createForm(options, globalStore, groupName) { | ||
function createForm(options, globalStore, groupName, initialData) { | ||
/** | ||
@@ -1185,3 +1193,3 @@ * Store for all keyup handlers than need removed when destroyed | ||
var dirtyHandlers = new Set(); | ||
var stores = createFormStores(options); | ||
var stores = createFormStores(options, initialData); | ||
var isGroup = typeof groupName !== 'undefined'; | ||
@@ -1206,3 +1214,4 @@ var initialOptions = options; | ||
groupedMap = __spread(formElements.reduce(function (entryMap, e) { | ||
var name = e.getAttribute('name'); | ||
var beakerKey = e.dataset.beakerKey; | ||
var name = beakerKey || e.getAttribute('name'); | ||
return entryMap.set(name, __spread(entryMap.get(name) || [], [e])); | ||
@@ -1264,3 +1273,3 @@ }, new Map())); | ||
if (node instanceof HTMLFormElement) { | ||
submitHandler = createSubmitHandler(stores); | ||
submitHandler = createSubmitHandler(stores, node); | ||
node.addEventListener('submit', submitHandler); | ||
@@ -1371,3 +1380,3 @@ } | ||
function createGroup(options, beakerStores) { | ||
var stores = createGroupStores(options); | ||
var groupStores = createGroupStores(options); | ||
var groupName; | ||
@@ -1382,2 +1391,3 @@ var globalObserver; | ||
var formInstances = new Map(); | ||
var subscriptions = new Set(); | ||
/** | ||
@@ -1391,52 +1401,79 @@ * Called when the group forms need destroyed | ||
}); | ||
subscriptions.forEach(function (sub) { | ||
return sub(); | ||
}); | ||
formInstances.clear(); | ||
formulaInstances.clear(); | ||
subscriptions.clear(); | ||
} | ||
/** | ||
* Called when a node it removed, it destroys it's form instance | ||
* @param removedNodes | ||
* Cleanup stores if there has been items removed, this will be updated with new store data | ||
* @param rows | ||
*/ | ||
function nodesRemoved(removedNodes) { | ||
for (var i = 0; i < removedNodes.length; i++) { | ||
var row = removedNodes[i]; | ||
var instance = formInstances.get(row); | ||
function cleanupStores(rows) { | ||
Object.keys(groupStores).forEach(function (key) { | ||
if (['formValues', 'initialValues', 'submitValues'].includes(key)) return; | ||
groupStores[key].update(function (state) { | ||
return Array.isArray(state) ? state.slice(0, rows.length) : state; | ||
}); | ||
}); | ||
} | ||
/** | ||
* Setup subscriptions to child stores when we re-create each form | ||
* @param form | ||
* @param index | ||
*/ | ||
if (instance) { | ||
instance.destroy(); | ||
} | ||
formInstances["delete"](row); | ||
formulaInstances["delete"](row); | ||
} | ||
function setupSubscriptions(form, index) { | ||
var initial = true; | ||
Object.entries(form.stores).forEach(function (_a) { | ||
var _b = __read(_a, 2), | ||
key = _b[0], | ||
store = _b[1]; | ||
stores.isFormReady.set(true); | ||
var unsub = store.subscribe(function (value) { | ||
if (initial && key === 'formValues') return; //Don't emit the form values when there is a form value change from the group | ||
groupStores[key].update(function (state) { | ||
if (Array.isArray(state)) { | ||
state.splice(index, 1, value); | ||
return state; | ||
} | ||
return value; | ||
}); | ||
}); | ||
subscriptions.add(unsub); | ||
}); | ||
initial = false; | ||
} | ||
/** | ||
* Called when a node is added, creates a new instance of a form for the row | ||
* @param addedNodes | ||
* Handle the change in data in the group, recreate the required forms | ||
* @param rows | ||
*/ | ||
function nodesAdded(addedNodes) { | ||
for (var i = 0; i < addedNodes.length; i++) { | ||
var row = addedNodes[i]; | ||
function groupHasChanged(rows) { | ||
groupStores.isFormReady.set(false); | ||
var currentVals = get(groupStores.formValues); | ||
destroyGroup(); | ||
cleanupStores(rows); | ||
var opts = __assign({}, formulaOptions); | ||
if (defaultValues && defaultValues[i]) { | ||
opts.defaultValues = defaultValues[i]; | ||
} | ||
var form = createForm(opts, undefined, groupName); | ||
for (var i = 0; i < rows.length; i++) { | ||
var row = rows[i]; | ||
row.setAttribute('data-beaker-index', "" + i); | ||
var form = createForm(formulaOptions, undefined, groupName, currentVals[i]); | ||
var instance = form.form(row); | ||
formulaInstances.set(row, form); | ||
var instance = form.form(row, true); | ||
formInstances.set(row, instance); | ||
setupSubscriptions(form, i); | ||
} | ||
stores.isFormReady.set(true); | ||
groupStores.isFormReady.set(true); | ||
} | ||
/** | ||
* Set up the Observer for the group | ||
* Set up an observer but use a query for the current scopes child nodes | ||
* @param node | ||
@@ -1447,10 +1484,5 @@ */ | ||
function setupGroupContainer(node) { | ||
globalObserver = new MutationObserver(function (records) { | ||
stores.isFormReady.set(false); | ||
if (records[0].addedNodes.length > 0) { | ||
nodesAdded(Array.from(records[0].addedNodes)); | ||
} else if (records[0].removedNodes.length > 0) { | ||
nodesRemoved(Array.from(records[0].removedNodes)); | ||
} | ||
globalObserver = new MutationObserver(function (mutations) { | ||
var rows = node.querySelectorAll(':scope > *'); | ||
groupHasChanged(Array.from(rows)); | ||
}); | ||
@@ -1461,3 +1493,3 @@ globalObserver.observe(node, { | ||
var rows = node.querySelectorAll(':scope > *'); | ||
nodesAdded(Array.from(rows)); | ||
groupHasChanged(Array.from(rows)); | ||
} | ||
@@ -1469,3 +1501,3 @@ | ||
groupName = node.id; | ||
beakerStores.set(groupName, stores); | ||
beakerStores.set(groupName, groupStores); | ||
} else { | ||
@@ -1508,8 +1540,8 @@ groupName = "beaker-group-" + groupCounter++; | ||
forms: formulaInstances, | ||
stores: stores, | ||
stores: groupStores, | ||
init: function init(items) { | ||
return stores.formValues.set(items); | ||
return groupStores.formValues.set(items); | ||
}, | ||
add: function add(item) { | ||
return stores.formValues.update(function (state) { | ||
return groupStores.formValues.update(function (state) { | ||
return __spread(state, [item]); | ||
@@ -1519,3 +1551,3 @@ }); | ||
set: function set(index, item) { | ||
return stores.formValues.update(function (state) { | ||
return groupStores.formValues.update(function (state) { | ||
state.splice(index, 1, item); | ||
@@ -1526,11 +1558,17 @@ return state; | ||
"delete": function _delete(index) { | ||
return stores.formValues.update(function (state) { | ||
state.splice(index, 1); | ||
return state; | ||
return Object.keys(groupStores).forEach(function (key) { | ||
groupStores[key].update(function (state) { | ||
if (Array.isArray(state)) { | ||
state.splice(index, 1); | ||
return state; | ||
} | ||
return state; | ||
}); | ||
}); | ||
}, | ||
clear: function clear() { | ||
return stores.formValues.set([]); | ||
return groupStores.formValues.set([]); | ||
} | ||
}, stores); | ||
}, groupStores); | ||
} | ||
@@ -1537,0 +1575,0 @@ |
@@ -208,3 +208,3 @@ (function (global, factory) { | ||
function createValidationChecker(inputGroup, options) { | ||
function createValidationChecker(inputGroup, elementGroup, options) { | ||
/** | ||
@@ -224,4 +224,6 @@ * Method called each time a field is updated | ||
el.setCustomValidity(''); | ||
el.removeAttribute('data-formula-invalid'); // If there's no options, just return the current error | ||
elementGroup.forEach(function (gel) { | ||
gel.setCustomValidity(''); | ||
gel.removeAttribute('data-formula-invalid'); | ||
}); // If there's no options, just return the current error | ||
@@ -512,3 +514,3 @@ if (!options) { | ||
function createFieldExtract(name, elementGroup, options, stores) { | ||
var validator = createValidationChecker(name, options); | ||
var validator = createValidationChecker(name, elementGroup, options); | ||
@@ -543,4 +545,8 @@ var isMultiValue = function () { | ||
if (isInit && (isMultiValue || element.type === 'select-multiple')) { | ||
value = elValue.length === 0 ? value : elValue; | ||
if (isInit) { | ||
if (isMultiValue || element.type === 'select-multiple') { | ||
value = elValue.length === 0 ? value : elValue; | ||
} else if (typeof value === undefined) { | ||
value = elValue === null ? '' : elValue; | ||
} | ||
} else { | ||
@@ -684,5 +690,6 @@ value = elValue === null ? '' : elValue; | ||
function createSubmitHandler(stores) { | ||
function createSubmitHandler(stores, form) { | ||
return function () { | ||
return stores.formValues.subscribe(function (v) { | ||
form.reportValidity(); | ||
stores.formValues.subscribe(function (v) { | ||
return stores.submitValues.set(v); | ||
@@ -1178,5 +1185,6 @@ })(); | ||
* @param groupName | ||
* @param initialData | ||
*/ | ||
function createForm(options, globalStore, groupName) { | ||
function createForm(options, globalStore, groupName, initialData) { | ||
/** | ||
@@ -1189,3 +1197,3 @@ * Store for all keyup handlers than need removed when destroyed | ||
var dirtyHandlers = new Set(); | ||
var stores = createFormStores(options); | ||
var stores = createFormStores(options, initialData); | ||
var isGroup = typeof groupName !== 'undefined'; | ||
@@ -1210,3 +1218,4 @@ var initialOptions = options; | ||
groupedMap = __spread(formElements.reduce(function (entryMap, e) { | ||
var name = e.getAttribute('name'); | ||
var beakerKey = e.dataset.beakerKey; | ||
var name = beakerKey || e.getAttribute('name'); | ||
return entryMap.set(name, __spread(entryMap.get(name) || [], [e])); | ||
@@ -1268,3 +1277,3 @@ }, new Map())); | ||
if (node instanceof HTMLFormElement) { | ||
submitHandler = createSubmitHandler(stores); | ||
submitHandler = createSubmitHandler(stores, node); | ||
node.addEventListener('submit', submitHandler); | ||
@@ -1375,3 +1384,3 @@ } | ||
function createGroup(options, beakerStores) { | ||
var stores = createGroupStores(options); | ||
var groupStores = createGroupStores(options); | ||
var groupName; | ||
@@ -1386,2 +1395,3 @@ var globalObserver; | ||
var formInstances = new Map(); | ||
var subscriptions = new Set(); | ||
/** | ||
@@ -1395,52 +1405,79 @@ * Called when the group forms need destroyed | ||
}); | ||
subscriptions.forEach(function (sub) { | ||
return sub(); | ||
}); | ||
formInstances.clear(); | ||
formulaInstances.clear(); | ||
subscriptions.clear(); | ||
} | ||
/** | ||
* Called when a node it removed, it destroys it's form instance | ||
* @param removedNodes | ||
* Cleanup stores if there has been items removed, this will be updated with new store data | ||
* @param rows | ||
*/ | ||
function nodesRemoved(removedNodes) { | ||
for (var i = 0; i < removedNodes.length; i++) { | ||
var row = removedNodes[i]; | ||
var instance = formInstances.get(row); | ||
function cleanupStores(rows) { | ||
Object.keys(groupStores).forEach(function (key) { | ||
if (['formValues', 'initialValues', 'submitValues'].includes(key)) return; | ||
groupStores[key].update(function (state) { | ||
return Array.isArray(state) ? state.slice(0, rows.length) : state; | ||
}); | ||
}); | ||
} | ||
/** | ||
* Setup subscriptions to child stores when we re-create each form | ||
* @param form | ||
* @param index | ||
*/ | ||
if (instance) { | ||
instance.destroy(); | ||
} | ||
formInstances["delete"](row); | ||
formulaInstances["delete"](row); | ||
} | ||
function setupSubscriptions(form, index) { | ||
var initial = true; | ||
Object.entries(form.stores).forEach(function (_a) { | ||
var _b = __read(_a, 2), | ||
key = _b[0], | ||
store = _b[1]; | ||
stores.isFormReady.set(true); | ||
var unsub = store.subscribe(function (value) { | ||
if (initial && key === 'formValues') return; //Don't emit the form values when there is a form value change from the group | ||
groupStores[key].update(function (state) { | ||
if (Array.isArray(state)) { | ||
state.splice(index, 1, value); | ||
return state; | ||
} | ||
return value; | ||
}); | ||
}); | ||
subscriptions.add(unsub); | ||
}); | ||
initial = false; | ||
} | ||
/** | ||
* Called when a node is added, creates a new instance of a form for the row | ||
* @param addedNodes | ||
* Handle the change in data in the group, recreate the required forms | ||
* @param rows | ||
*/ | ||
function nodesAdded(addedNodes) { | ||
for (var i = 0; i < addedNodes.length; i++) { | ||
var row = addedNodes[i]; | ||
function groupHasChanged(rows) { | ||
groupStores.isFormReady.set(false); | ||
var currentVals = store.get(groupStores.formValues); | ||
destroyGroup(); | ||
cleanupStores(rows); | ||
var opts = __assign({}, formulaOptions); | ||
if (defaultValues && defaultValues[i]) { | ||
opts.defaultValues = defaultValues[i]; | ||
} | ||
var form = createForm(opts, undefined, groupName); | ||
for (var i = 0; i < rows.length; i++) { | ||
var row = rows[i]; | ||
row.setAttribute('data-beaker-index', "" + i); | ||
var form = createForm(formulaOptions, undefined, groupName, currentVals[i]); | ||
var instance = form.form(row); | ||
formulaInstances.set(row, form); | ||
var instance = form.form(row, true); | ||
formInstances.set(row, instance); | ||
setupSubscriptions(form, i); | ||
} | ||
stores.isFormReady.set(true); | ||
groupStores.isFormReady.set(true); | ||
} | ||
/** | ||
* Set up the Observer for the group | ||
* Set up an observer but use a query for the current scopes child nodes | ||
* @param node | ||
@@ -1451,10 +1488,5 @@ */ | ||
function setupGroupContainer(node) { | ||
globalObserver = new MutationObserver(function (records) { | ||
stores.isFormReady.set(false); | ||
if (records[0].addedNodes.length > 0) { | ||
nodesAdded(Array.from(records[0].addedNodes)); | ||
} else if (records[0].removedNodes.length > 0) { | ||
nodesRemoved(Array.from(records[0].removedNodes)); | ||
} | ||
globalObserver = new MutationObserver(function (mutations) { | ||
var rows = node.querySelectorAll(':scope > *'); | ||
groupHasChanged(Array.from(rows)); | ||
}); | ||
@@ -1465,3 +1497,3 @@ globalObserver.observe(node, { | ||
var rows = node.querySelectorAll(':scope > *'); | ||
nodesAdded(Array.from(rows)); | ||
groupHasChanged(Array.from(rows)); | ||
} | ||
@@ -1473,3 +1505,3 @@ | ||
groupName = node.id; | ||
beakerStores.set(groupName, stores); | ||
beakerStores.set(groupName, groupStores); | ||
} else { | ||
@@ -1512,8 +1544,8 @@ groupName = "beaker-group-" + groupCounter++; | ||
forms: formulaInstances, | ||
stores: stores, | ||
stores: groupStores, | ||
init: function init(items) { | ||
return stores.formValues.set(items); | ||
return groupStores.formValues.set(items); | ||
}, | ||
add: function add(item) { | ||
return stores.formValues.update(function (state) { | ||
return groupStores.formValues.update(function (state) { | ||
return __spread(state, [item]); | ||
@@ -1523,3 +1555,3 @@ }); | ||
set: function set(index, item) { | ||
return stores.formValues.update(function (state) { | ||
return groupStores.formValues.update(function (state) { | ||
state.splice(index, 1, item); | ||
@@ -1530,11 +1562,17 @@ return state; | ||
"delete": function _delete(index) { | ||
return stores.formValues.update(function (state) { | ||
state.splice(index, 1); | ||
return state; | ||
return Object.keys(groupStores).forEach(function (key) { | ||
groupStores[key].update(function (state) { | ||
if (Array.isArray(state)) { | ||
state.splice(index, 1); | ||
return state; | ||
} | ||
return state; | ||
}); | ||
}); | ||
}, | ||
clear: function clear() { | ||
return stores.formValues.set([]); | ||
return groupStores.formValues.set([]); | ||
} | ||
}, stores); | ||
}, groupStores); | ||
} | ||
@@ -1541,0 +1579,0 @@ |
@@ -57,3 +57,3 @@ import { Writable } from 'svelte/store'; | ||
*/ | ||
form: (node: HTMLElement, isGroup?: boolean) => { | ||
form: (node: HTMLElement) => { | ||
destroy: () => void; | ||
@@ -60,0 +60,0 @@ }; |
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
132616
3203