svelte-formula
Advanced tools
Comparing version 0.7.2 to 0.8.0
@@ -8,2 +8,39 @@ # Changelog | ||
## [0.8.0] - 2021-02-21 | ||
### Added | ||
- New `beaker` API that provides functionality for working with groups of data as multi-line forms. | ||
See [documentation](https://formula.svelte.codes/docs/beaker) for more details on use | ||
```sveltehtml | ||
import { beaker } from 'svelte-formula' | ||
const contacts = beaker(); | ||
export let loadedContacts = []; | ||
const contactValues = contacts.formValues; | ||
contacts.init(loadedContacts) // Set initial values | ||
// Easily add items to the group | ||
function addRow(item) { | ||
contacts.add({...item}); | ||
} | ||
//... or remove them | ||
function removeRow(index) { | ||
contacts.delete(index); | ||
} | ||
.... | ||
<div use:contacts.group> | ||
{#each $contactValues as contact, i} | ||
<!-- Template for each row --> | ||
{/each} | ||
</div> | ||
``` | ||
### Fixed | ||
- Some internal API fixes and refactoring | ||
## [0.7.2] 2021-02-19 | ||
@@ -10,0 +47,0 @@ |
@@ -1,4 +0,2 @@ | ||
import { FormulaError, FormValues } from './types/forms'; | ||
import { FormulaOptions } from './types/options'; | ||
import { Formula, FormulaStores } from './types/formula'; | ||
import { Beaker, BeakerStores, Formula, FormulaError, FormulaOptions, FormulaStores, FormValues } from './types'; | ||
export { FormulaError, FormValues, FormulaStores }; | ||
@@ -11,2 +9,3 @@ /** | ||
export declare const formulaStores: Map<string, FormulaStores>; | ||
export declare const beakerStores: Map<string, BeakerStores>; | ||
/** | ||
@@ -23,1 +22,7 @@ * The `formula` function returns a form object that can be bound to any HTML | ||
export declare function formula(options?: FormulaOptions): Formula; | ||
/** | ||
* The `beaker` function returns an object that allows for the creation of form groups within your main form | ||
* The methos returns a group | ||
* @param options | ||
*/ | ||
export declare function beaker(options?: FormulaOptions): Beaker; |
{ | ||
"name": "svelte-formula", | ||
"description": "Reactive Forms for Svelte", | ||
"version": "0.7.2", | ||
"version": "0.8.0", | ||
"keywords": [ | ||
@@ -6,0 +6,0 @@ "svelte", |
@@ -16,2 +16,5 @@ # Svelte Formula | ||
Formula also supports multi-row forms with [Beaker](https://formula.svelte.codes/docs/beaker), it's internal groups API | ||
for working with arrays of data. | ||
## Install Instructions | ||
@@ -23,3 +26,3 @@ | ||
All you need is a element container with the Svelte [use](https://svelte.dev/docs#use_action) directive and form input | ||
All you need is an element container with the Svelte [use](https://svelte.dev/docs#use_action) directive and form input | ||
fields with their `name` property set. | ||
@@ -26,0 +29,0 @@ |
@@ -77,4 +77,3 @@ import { get, writable } from 'svelte/store'; | ||
/** | ||
* Extract all fields with a `name` attribute, then only return the ones that have `checkValidity` as we use these | ||
* for our validity state | ||
* Extract all fields from the form that are valid inputs with `name` property that are not part of a form group | ||
* | ||
@@ -86,2 +85,13 @@ * @private | ||
function getAllFieldsWithValidity(rootEl) { | ||
var nodeList = rootEl.querySelectorAll('*[name]:not([role="beaker-group"] *[name])'); | ||
return Array.from(nodeList).filter(function (el) { | ||
return el.checkValidity; | ||
}); | ||
} | ||
/** | ||
* Extract all fields from a group that are valid inputs with `name` property | ||
* @param rootEl | ||
*/ | ||
function getGroupFields(rootEl) { | ||
var nodeList = rootEl.querySelectorAll('*[name]'); | ||
@@ -300,8 +310,6 @@ return Array.from(nodeList).filter(function (el) { | ||
} else if (element.type === 'checkbox') { | ||
elementGroup.forEach(function (el) { | ||
el.checked = value === el.value; | ||
}); | ||
element.checked = value; | ||
} else if (element.type === 'radio') { | ||
elementGroup.forEach(function (el) { | ||
el.checked = value === el.value; | ||
return el.checked = value === el.value; | ||
}); | ||
@@ -354,3 +362,6 @@ } else if (element.type === 'file') { | ||
{ | ||
elValue = element.checked ? element.value : null; | ||
var foundElement = elementGroup.find(function (el) { | ||
return el.checked; | ||
}); | ||
elValue = foundElement ? foundElement.value : null; | ||
break; | ||
@@ -402,3 +413,3 @@ } | ||
return function (element, isInit, isReset) { | ||
var _a, _b, _c, _d; | ||
var _a, _b, _c, _d, _e; | ||
@@ -428,2 +439,6 @@ var value; | ||
if ((_e = element.dataset) === null || _e === void 0 ? void 0 : _e.beakerKey) { | ||
name = element.dataset.beakerKey; | ||
} | ||
return __assign({ | ||
@@ -527,6 +542,7 @@ name: name, | ||
* @param options | ||
* @param isGroup | ||
*/ | ||
function createHandler(name, eventName, element, groupElements, stores, options) { | ||
function createHandler(name, eventName, element, groupElements, stores, options, isGroup) { | ||
var _a; | ||
@@ -561,5 +577,5 @@ | ||
var initialValues = {}; | ||
var initialValidity = {}; | ||
var initialEnrichment = {}; | ||
var initialValues = new WeakMap(); | ||
var initialValidity = new WeakMap(); | ||
var initialEnrichment = new WeakMap(); | ||
/** | ||
@@ -577,2 +593,6 @@ * Initialise the stores with data from the form, it will also use any default values provided | ||
var formValues = {}; | ||
var validityValues = {}; | ||
var enrichmentValues = {}; | ||
try { | ||
@@ -591,8 +611,8 @@ for (var allGroups_1 = __values(allGroups), allGroups_1_1 = allGroups_1.next(); !allGroups_1_1.done; allGroups_1_1 = allGroups_1.next()) { | ||
initialValues[key] = value; | ||
initialValidity[key] = validity; | ||
formValues[name_1] = value; | ||
validityValues[name_1] = validity; | ||
if ((_b = options === null || options === void 0 ? void 0 : options.enrich) === null || _b === void 0 ? void 0 : _b[key]) { | ||
var enrich = createEnrichField(key, options); | ||
initialEnrichment[key] = enrich(value); | ||
if ((_b = options === null || options === void 0 ? void 0 : options.enrich) === null || _b === void 0 ? void 0 : _b[name_1]) { | ||
var enrich = createEnrichField(name_1, options); | ||
enrichmentValues[name_1] = enrich(value); | ||
} | ||
@@ -612,9 +632,12 @@ } | ||
stores.formValues.set(initialValues); | ||
stores.initialValues.set(initialValues); | ||
stores.validity.set(initialValidity); | ||
stores.isFormValid.set(Object.values(initialValidity).every(function (v) { | ||
stores.formValues.set(formValues); | ||
stores.initialValues.set(formValues); | ||
stores.validity.set(validityValues); | ||
stores.isFormValid.set(Object.values(validityValues).every(function (v) { | ||
return v.valid; | ||
})); | ||
stores.enrichment.set(initialEnrichment); | ||
stores.enrichment.set(enrichmentValues); | ||
initialValues.set(stores, formValues); | ||
initialValidity.set(stores, validityValues); | ||
initialEnrichment.set(stores, enrichmentValues); | ||
} | ||
@@ -626,3 +649,3 @@ /** | ||
function createReset(allGroups, stores, options) { | ||
function createReset(allGroups, stores, options, isGroup) { | ||
/** | ||
@@ -634,8 +657,11 @@ * Resets the form to the initial values | ||
stores.formValues.set(initialValues); | ||
stores.validity.set(initialValidity); | ||
stores.isFormValid.set(Object.values(initialValidity).every(function (v) { | ||
var formValues = initialValues.get(stores); | ||
var validityValues = initialValidity.get(stores); | ||
var enrichment = initialEnrichment.get(stores); | ||
stores.formValues.set(formValues); | ||
stores.validity.set(validityValues); | ||
stores.isFormValid.set(Object.values(validityValues).every(function (v) { | ||
return v.valid; | ||
})); | ||
stores.enrichment.set(initialEnrichment); // Also override touched and dirty | ||
stores.enrichment.set(enrichment); // Also override touched and dirty | ||
@@ -909,3 +935,3 @@ stores.touched.set(Object.keys(initialValues).reduce(function (val, key) { | ||
var dirtyHandlers = new Set(); | ||
var initialOptions = options; | ||
var currentOptions = options; | ||
var submitHandler = undefined; | ||
@@ -919,6 +945,7 @@ var unsub; | ||
* @param innerOpt | ||
* @param isGroup | ||
*/ | ||
function bindElements(node, innerOpt) { | ||
var formElements = getAllFieldsWithValidity(node); // Group elements by name | ||
function bindElements(node, innerOpt, isGroup) { | ||
var formElements = isGroup ? getGroupFields(node) : getAllFieldsWithValidity(node); // Group elements by name | ||
@@ -971,3 +998,3 @@ groupedMap = __spread(formElements.reduce(function (entryMap, e) { | ||
if (node.id) { | ||
if (node.id && globalStore) { | ||
globalStore.set(node.id, stores); | ||
@@ -1018,12 +1045,9 @@ } // If the HTML element attached is a form, also listen for the submit event | ||
return { | ||
create: function create(node) { | ||
create: function create(node, isGroup) { | ||
currentNode = node; | ||
bindElements(node, options); | ||
bindElements(node, options, isGroup); | ||
return { | ||
destroy: function destroy() { | ||
cleanupSubscriptions(); | ||
if (currentNode.id) { | ||
globalStore["delete"](name); | ||
} | ||
currentNode.id && globalStore && globalStore["delete"](name); | ||
} | ||
@@ -1035,3 +1059,8 @@ }; | ||
cleanupSubscriptions(); | ||
bindElements(currentNode, updatedOpts || initialOptions); | ||
if (updatedOpts) { | ||
currentOptions = updatedOpts; | ||
} | ||
bindElements(currentNode, currentOptions); | ||
}, | ||
@@ -1041,6 +1070,3 @@ destroy: function destroy() { | ||
cleanupSubscriptions(); | ||
if (currentNode.id) { | ||
globalStore["delete"](name); | ||
} | ||
currentNode.id && globalStore && globalStore["delete"](name); | ||
}, | ||
@@ -1062,4 +1088,4 @@ reset: function reset() { | ||
}); | ||
bindElements(currentNode, initialOptions); | ||
} | ||
}, | ||
stores: stores | ||
}; | ||
@@ -1069,16 +1095,26 @@ } | ||
/** | ||
* Create the stores for the the form instance, these can be set using the `defaultValue` property | ||
* of FormulaOptions | ||
* @param options | ||
* Function to create initial state values for the store using any passed default values, this is not the final initial | ||
* state, but is used when the stores are created to ensure required keys are available with any initial state, even if | ||
* an empty string. | ||
* | ||
* @returns An object containing the stores for the form instance | ||
* This avoids the need for the `?.` operator in code since we can guarantee the key on first subscription this way | ||
* | ||
* @private | ||
* @internal | ||
* | ||
* @param options Initial options to use | ||
* @param initialData | ||
*/ | ||
function createStores(options) { | ||
var initialKeys = Object.keys((options === null || options === void 0 ? void 0 : options.defaultValues) || {}); | ||
var initialStates = initialKeys.reduce(function (val, key) { | ||
function createFirstState(options, initialData) { | ||
var initialValues = (options === null || options === void 0 ? void 0 : options.defaultValues) || {}; | ||
initialValues = __assign(__assign({}, initialValues), initialData); | ||
var initialKeys = Object.keys(initialValues); // Generate from default values any initial touched and dirty states | ||
var initialFieldState = initialKeys.reduce(function (val, key) { | ||
var _a; | ||
return __assign(__assign({}, val), (_a = {}, _a[key] = false, _a)); | ||
}, {}); | ||
}, {}); // Generate from default values any initial initial validity | ||
var initialValidity = initialKeys.reduce(function (val, key) { | ||
@@ -1093,3 +1129,4 @@ var _a; | ||
}, _a)); | ||
}, {}); | ||
}, {}); // Generate from default values any initial form validity | ||
var initialFormValidity = Object.keys((options === null || options === void 0 ? void 0 : options.formValidators) || {}).reduce(function (val, key) { | ||
@@ -1099,3 +1136,4 @@ var _a; | ||
return __assign(__assign({}, val), (_a = {}, _a[key] = '', _a)); | ||
}, {}); | ||
}, {}); // Generate from default values any initial enrichment | ||
var initialEnrichment = Object.entries((options === null || options === void 0 ? void 0 : options.enrich) || {}).reduce(function (value, _a) { | ||
@@ -1121,16 +1159,188 @@ var _b; | ||
return { | ||
formValues: writable((options === null || options === void 0 ? void 0 : options.defaultValues) || {}), | ||
initialValues: initialValues, | ||
initialKeys: initialKeys, | ||
initialFieldState: initialFieldState, | ||
initialValidity: initialValidity, | ||
initialFormValidity: initialFormValidity, | ||
initialEnrichment: initialEnrichment | ||
}; | ||
} | ||
/** | ||
* Create the stores for the the form instance, these can be set using the `defaultValue` property | ||
* of FormulaOptions | ||
* @param options | ||
* @param initialData | ||
* | ||
* @returns An object containing the stores for the form instance | ||
*/ | ||
function createFormStores(options, initialData) { | ||
var initialStoreState = createFirstState(options, initialData); | ||
return { | ||
formValues: writable(initialStoreState.initialValues), | ||
submitValues: writable({}), | ||
initialValues: writable((options === null || options === void 0 ? void 0 : options.defaultValues) || {}), | ||
touched: writable(initialStates), | ||
dirty: writable(initialStates), | ||
validity: writable(initialValidity), | ||
formValidity: writable(initialFormValidity), | ||
initialValues: writable(initialStoreState.initialValues), | ||
touched: writable(initialStoreState.initialFieldState), | ||
dirty: writable(initialStoreState.initialFieldState), | ||
validity: writable(initialStoreState.initialValidity), | ||
formValidity: writable(initialStoreState.initialFormValidity), | ||
isFormValid: writable(false), | ||
isFormReady: writable(false), | ||
enrichment: writable(initialEnrichment) | ||
enrichment: writable(initialStoreState.initialEnrichment) | ||
}; | ||
} | ||
/** | ||
* Create a group store which contains arrays of form store values | ||
*/ | ||
function createGroupStores(options) { | ||
return { | ||
formValues: writable([]), | ||
submitValues: writable([]), | ||
initialValues: writable([]), | ||
touched: writable([]), | ||
dirty: writable([]), | ||
validity: writable([]), | ||
formValidity: writable({}), | ||
isFormValid: writable(false), | ||
isFormReady: writable(false), | ||
enrichment: writable([]) | ||
}; | ||
} | ||
/** | ||
* Creates a group, which is really just a collection of forms for row data | ||
* @param stores | ||
* @param options | ||
* @param beakerStores | ||
*/ | ||
function createGroup(stores, options, beakerStores) { | ||
var groupId; | ||
var groupParentNode; | ||
var globalObserver; | ||
var formSet = new Set(); | ||
var subscriptions = new Map(); | ||
function resetGroup() { | ||
__spread(subscriptions).forEach(function (_a) { | ||
var _b = __read(_a, 2), | ||
key = _b[0], | ||
subs = _b[1]; | ||
if (key === 'isFormValid' || key === 'isFormReady') { | ||
stores[key].set(false); | ||
} else { | ||
stores[key].set([]); | ||
} | ||
subs.forEach(function (sub) { | ||
return sub(); | ||
}); | ||
}); | ||
subscriptions.clear(); | ||
formSet.forEach(function (form) { | ||
return form.destroy(); | ||
}); | ||
formSet.clear(); | ||
} | ||
function groupHasChanged(rows) { | ||
resetGroup(); | ||
rows.forEach(function (row, i) { | ||
var data = {}; | ||
if (row.dataset.bindData) { | ||
try { | ||
data = JSON.parse(row.dataset.bindData); | ||
} catch (_a) {} | ||
} | ||
var formStore = createFormStores(options, data); | ||
var form = createForm(formStore, options); | ||
formSet.add(form.create(row, true)); | ||
Object.entries(formStore).forEach(function (_a) { | ||
var _b = __read(_a, 2), | ||
key = _b[0], | ||
store = _b[1]; | ||
var unsub = store.subscribe(function (value) { | ||
stores[key].update(function (state) { | ||
if (Array.isArray(state)) { | ||
state.splice(i, 1, value); | ||
} else { | ||
state = value; | ||
} | ||
return state; | ||
}); | ||
}); | ||
if (!subscriptions.has(key)) { | ||
subscriptions.set(key, [unsub]); | ||
} else { | ||
var subs = subscriptions.get(key); | ||
subs.push(unsub); | ||
subscriptions.set(key, subs); | ||
} | ||
}); | ||
}); | ||
stores.isFormReady.set(true); | ||
} | ||
function setupGroupContainer(node) { | ||
node.setAttribute('data-beaker-group', 'true'); | ||
node.setAttribute('role', 'beaker-group'); | ||
globalObserver = new MutationObserver(function (mutations) { | ||
var rows = node.querySelectorAll(':scope > *'); | ||
groupHasChanged(Array.from(rows)); | ||
}); | ||
globalObserver.observe(node, { | ||
childList: true | ||
}); | ||
var rows = node.querySelectorAll(':scope > *'); | ||
groupHasChanged(Array.from(rows)); | ||
} | ||
return { | ||
create: function create(node) { | ||
groupId = node.id; | ||
if (groupId) { | ||
beakerStores.set(groupId, stores); | ||
} | ||
groupParentNode = node; | ||
setupGroupContainer(node); | ||
return { | ||
destroy: function destroy() { | ||
if (groupId) { | ||
beakerStores["delete"](groupId); | ||
} | ||
resetGroup(); | ||
globalObserver.disconnect(); | ||
} | ||
}; | ||
}, | ||
update: function update(updatedOpts) {}, | ||
destroy: function destroy() { | ||
if (groupId) { | ||
beakerStores["delete"](groupId); | ||
} | ||
resetGroup(); | ||
globalObserver.disconnect(); | ||
}, | ||
reset: function reset() { | ||
resetGroup(); | ||
globalObserver.disconnect(); | ||
setupGroupContainer(groupParentNode); | ||
} | ||
}; | ||
} | ||
/** | ||
* A global map of stores for elements with an `id` property and the `use` directive, | ||
@@ -1142,2 +1352,3 @@ * if no ID is used the store is not added | ||
var formulaStores = new Map(); | ||
var beakerStores = new Map(); | ||
/** | ||
@@ -1155,4 +1366,3 @@ * The `formula` function returns a form object that can be bound to any HTML | ||
function formula(options) { | ||
// Create a store object for this instance, if there is an `id` on the element the stores will be added to formulaStores | ||
var stores = createStores(options); | ||
var stores = createFormStores(options); | ||
@@ -1169,6 +1379,45 @@ var _a = createForm(stores, options, formulaStores), | ||
destroyForm: destroy, | ||
resetForm: reset | ||
resetForm: reset, | ||
stores: stores | ||
}, stores); | ||
} | ||
/** | ||
* The `beaker` function returns an object that allows for the creation of form groups within your main form | ||
* The methos returns a group | ||
* @param options | ||
*/ | ||
export { formula, formulaStores }; | ||
function beaker(options) { | ||
var stores = createGroupStores(); | ||
var _a = createGroup(stores, options, beakerStores), | ||
create = _a.create, | ||
update = _a.update, | ||
destroy = _a.destroy, | ||
reset = _a.reset; | ||
return __assign(__assign({ | ||
group: create, | ||
update: update, | ||
destroy: destroy, | ||
reset: reset, | ||
stores: stores | ||
}, stores), { | ||
init: function init(items) { | ||
return stores.formValues.set(items); | ||
}, | ||
add: function add(item) { | ||
return stores.formValues.update(function (state) { | ||
return __spread(state, [item]); | ||
}); | ||
}, | ||
"delete": function _delete(index) { | ||
return stores.formValues.update(function (state) { | ||
state.splice(index, 1); | ||
return state; | ||
}); | ||
} | ||
}); | ||
} | ||
export { beaker, beakerStores, formula, formulaStores }; |
@@ -81,4 +81,3 @@ (function (global, factory) { | ||
/** | ||
* Extract all fields with a `name` attribute, then only return the ones that have `checkValidity` as we use these | ||
* for our validity state | ||
* Extract all fields from the form that are valid inputs with `name` property that are not part of a form group | ||
* | ||
@@ -90,2 +89,13 @@ * @private | ||
function getAllFieldsWithValidity(rootEl) { | ||
var nodeList = rootEl.querySelectorAll('*[name]:not([role="beaker-group"] *[name])'); | ||
return Array.from(nodeList).filter(function (el) { | ||
return el.checkValidity; | ||
}); | ||
} | ||
/** | ||
* Extract all fields from a group that are valid inputs with `name` property | ||
* @param rootEl | ||
*/ | ||
function getGroupFields(rootEl) { | ||
var nodeList = rootEl.querySelectorAll('*[name]'); | ||
@@ -304,8 +314,6 @@ return Array.from(nodeList).filter(function (el) { | ||
} else if (element.type === 'checkbox') { | ||
elementGroup.forEach(function (el) { | ||
el.checked = value === el.value; | ||
}); | ||
element.checked = value; | ||
} else if (element.type === 'radio') { | ||
elementGroup.forEach(function (el) { | ||
el.checked = value === el.value; | ||
return el.checked = value === el.value; | ||
}); | ||
@@ -358,3 +366,6 @@ } else if (element.type === 'file') { | ||
{ | ||
elValue = element.checked ? element.value : null; | ||
var foundElement = elementGroup.find(function (el) { | ||
return el.checked; | ||
}); | ||
elValue = foundElement ? foundElement.value : null; | ||
break; | ||
@@ -406,3 +417,3 @@ } | ||
return function (element, isInit, isReset) { | ||
var _a, _b, _c, _d; | ||
var _a, _b, _c, _d, _e; | ||
@@ -432,2 +443,6 @@ var value; | ||
if ((_e = element.dataset) === null || _e === void 0 ? void 0 : _e.beakerKey) { | ||
name = element.dataset.beakerKey; | ||
} | ||
return __assign({ | ||
@@ -531,6 +546,7 @@ name: name, | ||
* @param options | ||
* @param isGroup | ||
*/ | ||
function createHandler(name, eventName, element, groupElements, stores, options) { | ||
function createHandler(name, eventName, element, groupElements, stores, options, isGroup) { | ||
var _a; | ||
@@ -565,5 +581,5 @@ | ||
var initialValues = {}; | ||
var initialValidity = {}; | ||
var initialEnrichment = {}; | ||
var initialValues = new WeakMap(); | ||
var initialValidity = new WeakMap(); | ||
var initialEnrichment = new WeakMap(); | ||
/** | ||
@@ -581,2 +597,6 @@ * Initialise the stores with data from the form, it will also use any default values provided | ||
var formValues = {}; | ||
var validityValues = {}; | ||
var enrichmentValues = {}; | ||
try { | ||
@@ -595,8 +615,8 @@ for (var allGroups_1 = __values(allGroups), allGroups_1_1 = allGroups_1.next(); !allGroups_1_1.done; allGroups_1_1 = allGroups_1.next()) { | ||
initialValues[key] = value; | ||
initialValidity[key] = validity; | ||
formValues[name_1] = value; | ||
validityValues[name_1] = validity; | ||
if ((_b = options === null || options === void 0 ? void 0 : options.enrich) === null || _b === void 0 ? void 0 : _b[key]) { | ||
var enrich = createEnrichField(key, options); | ||
initialEnrichment[key] = enrich(value); | ||
if ((_b = options === null || options === void 0 ? void 0 : options.enrich) === null || _b === void 0 ? void 0 : _b[name_1]) { | ||
var enrich = createEnrichField(name_1, options); | ||
enrichmentValues[name_1] = enrich(value); | ||
} | ||
@@ -616,9 +636,12 @@ } | ||
stores.formValues.set(initialValues); | ||
stores.initialValues.set(initialValues); | ||
stores.validity.set(initialValidity); | ||
stores.isFormValid.set(Object.values(initialValidity).every(function (v) { | ||
stores.formValues.set(formValues); | ||
stores.initialValues.set(formValues); | ||
stores.validity.set(validityValues); | ||
stores.isFormValid.set(Object.values(validityValues).every(function (v) { | ||
return v.valid; | ||
})); | ||
stores.enrichment.set(initialEnrichment); | ||
stores.enrichment.set(enrichmentValues); | ||
initialValues.set(stores, formValues); | ||
initialValidity.set(stores, validityValues); | ||
initialEnrichment.set(stores, enrichmentValues); | ||
} | ||
@@ -630,3 +653,3 @@ /** | ||
function createReset(allGroups, stores, options) { | ||
function createReset(allGroups, stores, options, isGroup) { | ||
/** | ||
@@ -638,8 +661,11 @@ * Resets the form to the initial values | ||
stores.formValues.set(initialValues); | ||
stores.validity.set(initialValidity); | ||
stores.isFormValid.set(Object.values(initialValidity).every(function (v) { | ||
var formValues = initialValues.get(stores); | ||
var validityValues = initialValidity.get(stores); | ||
var enrichment = initialEnrichment.get(stores); | ||
stores.formValues.set(formValues); | ||
stores.validity.set(validityValues); | ||
stores.isFormValid.set(Object.values(validityValues).every(function (v) { | ||
return v.valid; | ||
})); | ||
stores.enrichment.set(initialEnrichment); // Also override touched and dirty | ||
stores.enrichment.set(enrichment); // Also override touched and dirty | ||
@@ -913,3 +939,3 @@ stores.touched.set(Object.keys(initialValues).reduce(function (val, key) { | ||
var dirtyHandlers = new Set(); | ||
var initialOptions = options; | ||
var currentOptions = options; | ||
var submitHandler = undefined; | ||
@@ -923,6 +949,7 @@ var unsub; | ||
* @param innerOpt | ||
* @param isGroup | ||
*/ | ||
function bindElements(node, innerOpt) { | ||
var formElements = getAllFieldsWithValidity(node); // Group elements by name | ||
function bindElements(node, innerOpt, isGroup) { | ||
var formElements = isGroup ? getGroupFields(node) : getAllFieldsWithValidity(node); // Group elements by name | ||
@@ -975,3 +1002,3 @@ groupedMap = __spread(formElements.reduce(function (entryMap, e) { | ||
if (node.id) { | ||
if (node.id && globalStore) { | ||
globalStore.set(node.id, stores); | ||
@@ -1022,12 +1049,9 @@ } // If the HTML element attached is a form, also listen for the submit event | ||
return { | ||
create: function create(node) { | ||
create: function create(node, isGroup) { | ||
currentNode = node; | ||
bindElements(node, options); | ||
bindElements(node, options, isGroup); | ||
return { | ||
destroy: function destroy() { | ||
cleanupSubscriptions(); | ||
if (currentNode.id) { | ||
globalStore["delete"](name); | ||
} | ||
currentNode.id && globalStore && globalStore["delete"](name); | ||
} | ||
@@ -1039,3 +1063,8 @@ }; | ||
cleanupSubscriptions(); | ||
bindElements(currentNode, updatedOpts || initialOptions); | ||
if (updatedOpts) { | ||
currentOptions = updatedOpts; | ||
} | ||
bindElements(currentNode, currentOptions); | ||
}, | ||
@@ -1045,6 +1074,3 @@ destroy: function destroy() { | ||
cleanupSubscriptions(); | ||
if (currentNode.id) { | ||
globalStore["delete"](name); | ||
} | ||
currentNode.id && globalStore && globalStore["delete"](name); | ||
}, | ||
@@ -1066,4 +1092,4 @@ reset: function reset() { | ||
}); | ||
bindElements(currentNode, initialOptions); | ||
} | ||
}, | ||
stores: stores | ||
}; | ||
@@ -1073,16 +1099,26 @@ } | ||
/** | ||
* Create the stores for the the form instance, these can be set using the `defaultValue` property | ||
* of FormulaOptions | ||
* @param options | ||
* Function to create initial state values for the store using any passed default values, this is not the final initial | ||
* state, but is used when the stores are created to ensure required keys are available with any initial state, even if | ||
* an empty string. | ||
* | ||
* @returns An object containing the stores for the form instance | ||
* This avoids the need for the `?.` operator in code since we can guarantee the key on first subscription this way | ||
* | ||
* @private | ||
* @internal | ||
* | ||
* @param options Initial options to use | ||
* @param initialData | ||
*/ | ||
function createStores(options) { | ||
var initialKeys = Object.keys((options === null || options === void 0 ? void 0 : options.defaultValues) || {}); | ||
var initialStates = initialKeys.reduce(function (val, key) { | ||
function createFirstState(options, initialData) { | ||
var initialValues = (options === null || options === void 0 ? void 0 : options.defaultValues) || {}; | ||
initialValues = __assign(__assign({}, initialValues), initialData); | ||
var initialKeys = Object.keys(initialValues); // Generate from default values any initial touched and dirty states | ||
var initialFieldState = initialKeys.reduce(function (val, key) { | ||
var _a; | ||
return __assign(__assign({}, val), (_a = {}, _a[key] = false, _a)); | ||
}, {}); | ||
}, {}); // Generate from default values any initial initial validity | ||
var initialValidity = initialKeys.reduce(function (val, key) { | ||
@@ -1097,3 +1133,4 @@ var _a; | ||
}, _a)); | ||
}, {}); | ||
}, {}); // Generate from default values any initial form validity | ||
var initialFormValidity = Object.keys((options === null || options === void 0 ? void 0 : options.formValidators) || {}).reduce(function (val, key) { | ||
@@ -1103,3 +1140,4 @@ var _a; | ||
return __assign(__assign({}, val), (_a = {}, _a[key] = '', _a)); | ||
}, {}); | ||
}, {}); // Generate from default values any initial enrichment | ||
var initialEnrichment = Object.entries((options === null || options === void 0 ? void 0 : options.enrich) || {}).reduce(function (value, _a) { | ||
@@ -1125,16 +1163,188 @@ var _b; | ||
return { | ||
formValues: store.writable((options === null || options === void 0 ? void 0 : options.defaultValues) || {}), | ||
initialValues: initialValues, | ||
initialKeys: initialKeys, | ||
initialFieldState: initialFieldState, | ||
initialValidity: initialValidity, | ||
initialFormValidity: initialFormValidity, | ||
initialEnrichment: initialEnrichment | ||
}; | ||
} | ||
/** | ||
* Create the stores for the the form instance, these can be set using the `defaultValue` property | ||
* of FormulaOptions | ||
* @param options | ||
* @param initialData | ||
* | ||
* @returns An object containing the stores for the form instance | ||
*/ | ||
function createFormStores(options, initialData) { | ||
var initialStoreState = createFirstState(options, initialData); | ||
return { | ||
formValues: store.writable(initialStoreState.initialValues), | ||
submitValues: store.writable({}), | ||
initialValues: store.writable((options === null || options === void 0 ? void 0 : options.defaultValues) || {}), | ||
touched: store.writable(initialStates), | ||
dirty: store.writable(initialStates), | ||
validity: store.writable(initialValidity), | ||
formValidity: store.writable(initialFormValidity), | ||
initialValues: store.writable(initialStoreState.initialValues), | ||
touched: store.writable(initialStoreState.initialFieldState), | ||
dirty: store.writable(initialStoreState.initialFieldState), | ||
validity: store.writable(initialStoreState.initialValidity), | ||
formValidity: store.writable(initialStoreState.initialFormValidity), | ||
isFormValid: store.writable(false), | ||
isFormReady: store.writable(false), | ||
enrichment: store.writable(initialEnrichment) | ||
enrichment: store.writable(initialStoreState.initialEnrichment) | ||
}; | ||
} | ||
/** | ||
* Create a group store which contains arrays of form store values | ||
*/ | ||
function createGroupStores(options) { | ||
return { | ||
formValues: store.writable([]), | ||
submitValues: store.writable([]), | ||
initialValues: store.writable([]), | ||
touched: store.writable([]), | ||
dirty: store.writable([]), | ||
validity: store.writable([]), | ||
formValidity: store.writable({}), | ||
isFormValid: store.writable(false), | ||
isFormReady: store.writable(false), | ||
enrichment: store.writable([]) | ||
}; | ||
} | ||
/** | ||
* Creates a group, which is really just a collection of forms for row data | ||
* @param stores | ||
* @param options | ||
* @param beakerStores | ||
*/ | ||
function createGroup(stores, options, beakerStores) { | ||
var groupId; | ||
var groupParentNode; | ||
var globalObserver; | ||
var formSet = new Set(); | ||
var subscriptions = new Map(); | ||
function resetGroup() { | ||
__spread(subscriptions).forEach(function (_a) { | ||
var _b = __read(_a, 2), | ||
key = _b[0], | ||
subs = _b[1]; | ||
if (key === 'isFormValid' || key === 'isFormReady') { | ||
stores[key].set(false); | ||
} else { | ||
stores[key].set([]); | ||
} | ||
subs.forEach(function (sub) { | ||
return sub(); | ||
}); | ||
}); | ||
subscriptions.clear(); | ||
formSet.forEach(function (form) { | ||
return form.destroy(); | ||
}); | ||
formSet.clear(); | ||
} | ||
function groupHasChanged(rows) { | ||
resetGroup(); | ||
rows.forEach(function (row, i) { | ||
var data = {}; | ||
if (row.dataset.bindData) { | ||
try { | ||
data = JSON.parse(row.dataset.bindData); | ||
} catch (_a) {} | ||
} | ||
var formStore = createFormStores(options, data); | ||
var form = createForm(formStore, options); | ||
formSet.add(form.create(row, true)); | ||
Object.entries(formStore).forEach(function (_a) { | ||
var _b = __read(_a, 2), | ||
key = _b[0], | ||
store = _b[1]; | ||
var unsub = store.subscribe(function (value) { | ||
stores[key].update(function (state) { | ||
if (Array.isArray(state)) { | ||
state.splice(i, 1, value); | ||
} else { | ||
state = value; | ||
} | ||
return state; | ||
}); | ||
}); | ||
if (!subscriptions.has(key)) { | ||
subscriptions.set(key, [unsub]); | ||
} else { | ||
var subs = subscriptions.get(key); | ||
subs.push(unsub); | ||
subscriptions.set(key, subs); | ||
} | ||
}); | ||
}); | ||
stores.isFormReady.set(true); | ||
} | ||
function setupGroupContainer(node) { | ||
node.setAttribute('data-beaker-group', 'true'); | ||
node.setAttribute('role', 'beaker-group'); | ||
globalObserver = new MutationObserver(function (mutations) { | ||
var rows = node.querySelectorAll(':scope > *'); | ||
groupHasChanged(Array.from(rows)); | ||
}); | ||
globalObserver.observe(node, { | ||
childList: true | ||
}); | ||
var rows = node.querySelectorAll(':scope > *'); | ||
groupHasChanged(Array.from(rows)); | ||
} | ||
return { | ||
create: function create(node) { | ||
groupId = node.id; | ||
if (groupId) { | ||
beakerStores.set(groupId, stores); | ||
} | ||
groupParentNode = node; | ||
setupGroupContainer(node); | ||
return { | ||
destroy: function destroy() { | ||
if (groupId) { | ||
beakerStores["delete"](groupId); | ||
} | ||
resetGroup(); | ||
globalObserver.disconnect(); | ||
} | ||
}; | ||
}, | ||
update: function update(updatedOpts) {}, | ||
destroy: function destroy() { | ||
if (groupId) { | ||
beakerStores["delete"](groupId); | ||
} | ||
resetGroup(); | ||
globalObserver.disconnect(); | ||
}, | ||
reset: function reset() { | ||
resetGroup(); | ||
globalObserver.disconnect(); | ||
setupGroupContainer(groupParentNode); | ||
} | ||
}; | ||
} | ||
/** | ||
* A global map of stores for elements with an `id` property and the `use` directive, | ||
@@ -1146,2 +1356,3 @@ * if no ID is used the store is not added | ||
var formulaStores = new Map(); | ||
var beakerStores = new Map(); | ||
/** | ||
@@ -1159,4 +1370,3 @@ * The `formula` function returns a form object that can be bound to any HTML | ||
function formula(options) { | ||
// Create a store object for this instance, if there is an `id` on the element the stores will be added to formulaStores | ||
var stores = createStores(options); | ||
var stores = createFormStores(options); | ||
@@ -1173,6 +1383,47 @@ var _a = createForm(stores, options, formulaStores), | ||
destroyForm: destroy, | ||
resetForm: reset | ||
resetForm: reset, | ||
stores: stores | ||
}, stores); | ||
} | ||
/** | ||
* The `beaker` function returns an object that allows for the creation of form groups within your main form | ||
* The methos returns a group | ||
* @param options | ||
*/ | ||
function beaker(options) { | ||
var stores = createGroupStores(); | ||
var _a = createGroup(stores, options, beakerStores), | ||
create = _a.create, | ||
update = _a.update, | ||
destroy = _a.destroy, | ||
reset = _a.reset; | ||
return __assign(__assign({ | ||
group: create, | ||
update: update, | ||
destroy: destroy, | ||
reset: reset, | ||
stores: stores | ||
}, stores), { | ||
init: function init(items) { | ||
return stores.formValues.set(items); | ||
}, | ||
add: function add(item) { | ||
return stores.formValues.update(function (state) { | ||
return __spread(state, [item]); | ||
}); | ||
}, | ||
"delete": function _delete(index) { | ||
return stores.formValues.update(function (state) { | ||
state.splice(index, 1); | ||
return state; | ||
}); | ||
} | ||
}); | ||
} | ||
exports.beaker = beaker; | ||
exports.beakerStores = beakerStores; | ||
exports.formula = formula; | ||
@@ -1179,0 +1430,0 @@ exports.formulaStores = formulaStores; |
@@ -63,3 +63,3 @@ import { Writable } from 'svelte/store'; | ||
*/ | ||
updateForm: (updatedOpts: FormulaOptions) => void; | ||
updateForm: (updatedOpts?: FormulaOptions) => void; | ||
/** | ||
@@ -73,2 +73,15 @@ * Destroy | ||
resetForm: () => void; | ||
/** | ||
* Stores | ||
*/ | ||
stores: FormulaStores; | ||
} | ||
export interface Form { | ||
create: (node: HTMLElement, isGroup?: boolean) => { | ||
destroy: () => void; | ||
}; | ||
update: (updatedOpts: FormulaOptions) => void; | ||
destroy: () => void; | ||
reset: () => void; | ||
stores: FormulaStores; | ||
} |
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
141628
25
2849
53