UNPKG

@conform-to/dom

Version:

A set of opinionated helpers built on top of the Constraint Validation API

214 lines (199 loc) 6.53 kB
/** * Construct a form data with the submitter value. * It utilizes the submitter argument on the FormData constructor from modern browsers * with fallback to append the submitter value in case it is not unsupported. * * @see https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData#parameters */ function getFormData(form, submitter) { var payload = new FormData(form, submitter); if (submitter && submitter.type === 'submit' && submitter.name !== '') { var entries = payload.getAll(submitter.name); // This assumes the submitter value to be always unique, which should be fine in most cases if (!entries.includes(submitter.value)) { payload.append(submitter.name, submitter.value); } } return payload; } /** * Returns the paths from a name based on the JS syntax convention * @example * ```js * const paths = getPaths('todos[0].content'); // ['todos', 0, 'content'] * ``` */ function getPaths(name) { if (!name) { return []; } return name.split(/\.|(\[\d*\])/).reduce((result, segment) => { if (typeof segment !== 'undefined' && segment !== '' && segment !== '__proto__' && segment !== 'constructor' && segment !== 'prototype') { if (segment.startsWith('[') && segment.endsWith(']')) { var index = segment.slice(1, -1); result.push(Number(index)); } else { result.push(segment); } } return result; }, []); } /** * Returns a formatted name from the paths based on the JS syntax convention * @example * ```js * const name = formatPaths(['todos', 0, 'content']); // "todos[0].content" * ``` */ function formatPaths(paths) { return paths.reduce((name, path) => { if (typeof path === 'number') { return "".concat(name, "[").concat(Number.isNaN(path) ? '' : path, "]"); } if (name === '' || path === '') { return [name, path].join(''); } return [name, path].join('.'); }, ''); } /** * Format based on a prefix and a path */ function formatName(prefix, path) { return typeof path !== 'undefined' ? formatPaths([...getPaths(prefix), path]) : prefix !== null && prefix !== void 0 ? prefix : ''; } /** * Check if a name match the prefix paths */ function isPrefix(name, prefix) { var paths = getPaths(name); var prefixPaths = getPaths(prefix); return paths.length >= prefixPaths.length && prefixPaths.every((path, index) => paths[index] === path); } /** * Compare the parent and child paths to get the relative paths * Returns null if the child paths do not start with the parent paths */ function getChildPaths(parentNameOrPaths, childName) { var parentPaths = typeof parentNameOrPaths === 'string' ? getPaths(parentNameOrPaths) : parentNameOrPaths; var childPaths = getPaths(childName); if (childPaths.length >= parentPaths.length && parentPaths.every((path, index) => childPaths[index] === path)) { return childPaths.slice(parentPaths.length); } return null; } /** * Assign a value to a target object by following the paths */ function setValue(target, name, valueFn) { var paths = getPaths(name); var length = paths.length; var lastIndex = length - 1; var index = -1; var pointer = target; while (pointer != null && ++index < length) { var key = paths[index]; var nextKey = paths[index + 1]; var newValue = index != lastIndex ? Object.prototype.hasOwnProperty.call(pointer, key) && pointer[key] !== null ? pointer[key] : typeof nextKey === 'number' ? [] : {} : valueFn(pointer[key]); pointer[key] = newValue; pointer = pointer[key]; } } /** * Retrive the value from a target object by following the paths */ function getValue(target, name) { var pointer = target; for (var path of getPaths(name)) { if (typeof pointer === 'undefined' || pointer == null) { break; } if (!Object.prototype.hasOwnProperty.call(pointer, path)) { return; } if (isPlainObject(pointer) && typeof path === 'string') { pointer = pointer[path]; } else if (Array.isArray(pointer) && typeof path === 'number') { pointer = pointer[path]; } else { return; } } return pointer; } /** * Check if the value is a plain object */ function isPlainObject(obj) { return !!obj && obj.constructor === Object && Object.getPrototypeOf(obj) === Object.prototype; } /** * Check if the value is a File */ function isFile(obj) { // Skip checking if File is not defined if (typeof File === 'undefined') { return false; } return obj instanceof File; } /** * Normalize value by removing empty object or array, empty string and null values */ function normalize(value) { var acceptFile = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; if (isPlainObject(value)) { var obj = Object.keys(value).sort().reduce((result, key) => { var data = normalize(value[key], acceptFile); if (typeof data !== 'undefined') { result[key] = data; } return result; }, {}); if (Object.keys(obj).length === 0) { return; } return obj; } if (Array.isArray(value)) { if (value.length === 0) { return undefined; } return value.map(item => normalize(item, acceptFile)); } if (typeof value === 'string' && value === '' || value === null || isFile(value) && (!acceptFile || value.size === 0)) { return; } return value; } /** * Flatten a tree into a dictionary */ function flatten(data) { var _options$resolve; var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var result = {}; var resolve = (_options$resolve = options.resolve) !== null && _options$resolve !== void 0 ? _options$resolve : data => data; function process(data, prefix) { var value = normalize(resolve(data)); if (typeof value !== 'undefined') { result[prefix] = value; } if (Array.isArray(data)) { for (var i = 0; i < data.length; i++) { process(data[i], "".concat(prefix, "[").concat(i, "]")); } } else if (isPlainObject(data)) { for (var [key, _value] of Object.entries(data)) { process(_value, prefix ? "".concat(prefix, ".").concat(key) : key); } } } if (data) { var _options$prefix; process(data, (_options$prefix = options.prefix) !== null && _options$prefix !== void 0 ? _options$prefix : ''); } return result; } export { flatten, formatName, formatPaths, getChildPaths, getFormData, getPaths, getValue, isFile, isPlainObject, isPrefix, normalize, setValue };