UNPKG

@conform-to/react

Version:

Conform view adapter for react

306 lines (292 loc) 12.8 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js'); var future = require('@conform-to/dom/future'); var util = require('./util.js'); var state = require('./state.js'); /** * Serializes intent to string format: "type" or "type(payload)". */ function serializeIntent(intent) { if (!intent.payload) { return intent.type; } return "".concat(intent.type, "(").concat(JSON.stringify(intent.payload), ")"); } /** * Parses serialized intent string back to intent object. */ function deserializeIntent(value) { var type = value; var payload; var serializedPayload; var openParenIndex = value.indexOf('('); if (openParenIndex > 0 && value[value.length - 1] === ')') { type = value.slice(0, openParenIndex); serializedPayload = value.slice(openParenIndex + 1, -1); } if (serializedPayload) { try { payload = JSON.parse(serializedPayload); } catch (_unused) { // Ignore the error } } return { type, payload }; } /** * Applies intent transformation to submission payload. * Returns modified payload or null for reset intent. */ function applyIntent(submission, options) { var _options$handlers, _handler$validatePayl, _handler$validatePayl2; if (!submission.intent) { return submission.payload; } var intent = deserializeIntent(submission.intent); var handlers = (_options$handlers = options === null || options === void 0 ? void 0 : options.handlers) !== null && _options$handlers !== void 0 ? _options$handlers : actionHandlers; var handler = handlers[intent.type]; if (handler && handler.onApply && ((_handler$validatePayl = (_handler$validatePayl2 = handler.validatePayload) === null || _handler$validatePayl2 === void 0 ? void 0 : _handler$validatePayl2.call(handler, intent.payload)) !== null && _handler$validatePayl !== void 0 ? _handler$validatePayl : true)) { return handler.onApply(submission.payload, intent.payload); } return submission.payload; } function insertItem(list, item, index) { list.splice(index, 0, item); } function removeItem(list, index) { list.splice(index, 1); } function reorderItems(list, fromIndex, toIndex) { list.splice(toIndex, 0, ...list.splice(fromIndex, 1)); } /** * Updates list keys by removing child keys and optionally transforming remaining keys. */ function updateListKeys() { var keys = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var keyToBeRemoved = arguments.length > 1 ? arguments[1] : undefined; var updateKey = arguments.length > 2 ? arguments[2] : undefined; var basePath = future.getPathSegments(keyToBeRemoved); return util.transformKeys(keys, field => { var _updateKey; return future.getRelativePath(field, basePath) !== null ? null : (_updateKey = updateKey === null || updateKey === void 0 ? void 0 : updateKey(field)) !== null && _updateKey !== void 0 ? _updateKey : field; }); } /** * Built-in action handlers for form intents: * - reset: clears form data * - validate: marks fields as touched for validation display * - update: updates specific field values * - insert/remove/reorder: manages array field operations */ var actionHandlers = { reset: { onApply() { return null; } }, validate: { validatePayload(name) { return util.isOptional(name, util.isString); }, onUpdate(state, _ref) { var _intent$payload; var { submission, intent, error } = _ref; var name = (_intent$payload = intent.payload) !== null && _intent$payload !== void 0 ? _intent$payload : ''; var basePath = future.getPathSegments(name); var touchedFields = util.appendUniqueItem(state.touchedFields, name); for (var field of submission.fields) { // Add all child fields to the touched fields too if (future.getRelativePath(field, basePath) !== null) { touchedFields = util.appendUniqueItem(touchedFields, field); } } // We couldn't find out all the fields from the FormData, e.g. unchecked checkboxes. // or fieldsets without any fields, but we can at least include missing // required fields based on the form error if (name === '' && error) { for (var _name of Object.keys(error.fieldErrors)) { touchedFields = util.appendUniqueItem(touchedFields, _name); } } return util.merge(state, { touchedFields }); } }, update: { validatePayload(options) { return future.isPlainObject(options) && util.isOptional(options.name, util.isString) && util.isOptional(options.index, util.isNumber) && !util.isUndefined(options.value); }, onApply(value, options) { return util.updateValueAtPath(value, future.appendPathSegment(options.name, options.index), options.value); }, onUpdate(state, _ref2) { var { type, submission, intent } = _ref2; var listKeys = state.listKeys; // Update the keys only for client updates to avoid double updates if there is no client validation if (type === 'client') { // TODO: Do we really need to update the keys here? var name = future.appendPathSegment(intent.payload.name, intent.payload.index); // Remove all child keys listKeys = name === '' ? {} : updateListKeys(state.listKeys, name); } var basePath = future.getPathSegments(intent.payload.name); var touchedFields = state.touchedFields; for (var field of submission.fields) { if (basePath.length === 0 || future.getRelativePath(field, basePath) !== null) { touchedFields = util.appendUniqueItem(touchedFields, field); } } return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, state), {}, { listKeys, touchedFields }); } }, insert: { validatePayload(options) { return future.isPlainObject(options) && util.isString(options.name) && util.isOptional(options.index, util.isNumber); }, onApply(value, options) { var _options$index; var list = Array.from(util.getArrayAtPath(value, options.name)); insertItem(list, options.defaultValue, (_options$index = options.index) !== null && _options$index !== void 0 ? _options$index : list.length); return util.updateValueAtPath(value, options.name, list); }, onUpdate(state$1, _ref3) { var _intent$payload$index; var { type, submission, intent } = _ref3; var currentValue = submission.payload; var list = util.getArrayAtPath(currentValue, intent.payload.name); var index = (_intent$payload$index = intent.payload.index) !== null && _intent$payload$index !== void 0 ? _intent$payload$index : list.length; var updateListIndex = util.createPathIndexUpdater(intent.payload.name, currentIndex => index <= currentIndex ? currentIndex + 1 : currentIndex); var touchedFields = util.appendUniqueItem(util.compactMap(state$1.touchedFields, updateListIndex), intent.payload.name); var keys = state$1.listKeys; // Update the keys only for client updates to avoid double updates if there is no client validation if (type === 'client') { var _state$listKeys$inten; var listKeys = Array.from((_state$listKeys$inten = state$1.listKeys[intent.payload.name]) !== null && _state$listKeys$inten !== void 0 ? _state$listKeys$inten : state.getDefaultListKey(state$1.resetKey, currentValue, intent.payload.name)); insertItem(listKeys, util.generateUniqueKey(), index); keys = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, updateListKeys(state$1.listKeys, future.appendPathSegment(intent.payload.name, index), updateListIndex)), {}, { // Update existing list keys [intent.payload.name]: listKeys }); } return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, state$1), {}, { listKeys: keys, touchedFields }); } }, remove: { validatePayload(options) { return future.isPlainObject(options) && util.isString(options.name) && util.isNumber(options.index); }, onApply(value, options) { var list = Array.from(util.getArrayAtPath(value, options.name)); removeItem(list, options.index); return util.updateValueAtPath(value, options.name, list); }, onUpdate(state$1, _ref4) { var { type, submission, intent } = _ref4; var currentValue = submission.payload; var updateListIndex = util.createPathIndexUpdater(intent.payload.name, currentIndex => { if (intent.payload.index === currentIndex) { return null; } return intent.payload.index < currentIndex ? currentIndex - 1 : currentIndex; }); var touchedFields = util.appendUniqueItem(util.compactMap(state$1.touchedFields, updateListIndex), intent.payload.name); var keys = state$1.listKeys; // Update the keys only for client updates to avoid double updates if there is no client validation if (type === 'client') { var _state$listKeys$inten2; var listKeys = Array.from((_state$listKeys$inten2 = state$1.listKeys[intent.payload.name]) !== null && _state$listKeys$inten2 !== void 0 ? _state$listKeys$inten2 : state.getDefaultListKey(state$1.resetKey, currentValue, intent.payload.name)); removeItem(listKeys, intent.payload.index); keys = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, updateListKeys(state$1.listKeys, future.appendPathSegment(intent.payload.name, intent.payload.index), updateListIndex)), {}, { // Update existing list keys [intent.payload.name]: listKeys }); } return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, state$1), {}, { listKeys: keys, touchedFields }); } }, reorder: { validatePayload(options) { return future.isPlainObject(options) && util.isString(options.name) && util.isNumber(options.from) && util.isNumber(options.to); }, onApply(value, options) { var list = Array.from(util.getArrayAtPath(value, options.name)); reorderItems(list, options.from, options.to); return util.updateValueAtPath(value, options.name, list); }, onUpdate(state$1, _ref5) { var { type, submission, intent } = _ref5; var currentValue = submission.payload; var updateListIndex = util.createPathIndexUpdater(intent.payload.name, currentIndex => { if (intent.payload.from === intent.payload.to) { return currentIndex; } if (currentIndex === intent.payload.from) { return intent.payload.to; } if (intent.payload.from < intent.payload.to) { return currentIndex > intent.payload.from && currentIndex <= intent.payload.to ? currentIndex - 1 : currentIndex; } return currentIndex >= intent.payload.to && currentIndex < intent.payload.from ? currentIndex + 1 : currentIndex; }); var touchedFields = util.appendUniqueItem(util.compactMap(state$1.touchedFields, updateListIndex), intent.payload.name); var keys = state$1.listKeys; // Update the keys only for client updates to avoid double updates if there is no client validation if (type === 'client') { var _state$listKeys$inten3; var listKeys = Array.from((_state$listKeys$inten3 = state$1.listKeys[intent.payload.name]) !== null && _state$listKeys$inten3 !== void 0 ? _state$listKeys$inten3 : state.getDefaultListKey(state$1.resetKey, currentValue, intent.payload.name)); reorderItems(listKeys, intent.payload.from, intent.payload.to); keys = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, updateListKeys(state$1.listKeys, future.appendPathSegment(intent.payload.name, intent.payload.from), updateListIndex)), {}, { // Update existing list keys [intent.payload.name]: listKeys }); } return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, state$1), {}, { listKeys: keys, touchedFields }); } } }; exports.actionHandlers = actionHandlers; exports.applyIntent = applyIntent; exports.deserializeIntent = deserializeIntent; exports.insertItem = insertItem; exports.removeItem = removeItem; exports.reorderItems = reorderItems; exports.serializeIntent = serializeIntent; exports.updateListKeys = updateListKeys;