UNPKG

@appbuckets/react-ui-smart-components

Version:

UI Extended Components that work with @appbuckets/react-client and @appbuckets/react-ui

527 lines (520 loc) 17.6 kB
'use strict'; var _tslib = require('../_virtual/_tslib.js'); var React = require('react'); var realyFastDeepClone = require('rfdc'); var yup = require('@hookform/resolvers/yup'); var Modal = require('@appbuckets/react-ui/Modal'); var HookedForm = require('@appbuckets/react-ui-forms/HookedForm'); var QuerySuspenseError = require('../QuerySuspenseError/QuerySuspenseError.js'); var useActionBuilder = require('./lib/useActionBuilder.js'); var useActionNotifications = require('./lib/useActionNotifications.js'); var assertUniqueComponentName = require('../utils/assertUniqueComponentName.js'); var defaultValuesFromYupSchema = require('./utils/defaultValuesFromYupSchema.js'); var FormBuiltProvider = require('./FormBuiltProvider.js'); function _interopDefaultLegacy(e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty( n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; }, } ); } }); } n['default'] = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/ _interopNamespace(React); var realyFastDeepClone__default = /*#__PURE__*/ _interopDefaultLegacy(realyFastDeepClone); var Modal__default = /*#__PURE__*/ _interopDefaultLegacy(Modal); var HookedForm__default = /*#__PURE__*/ _interopDefaultLegacy(HookedForm); /* -------- * Create a data cloner * -------- */ var dataCloner = realyFastDeepClone__default['default']({ circles: false, proto: false, }); /* -------- * Builder Function Definition * -------- */ function buildFormAction(configuration) { var _this = this; // ---- // Deconstruct base configuration object // ---- var // Strict // Content Element FormContent = configuration.Content, defaultDefinedDisplayName = configuration.displayName, // Buttons defaultDefinedCancelButton = configuration.cancelButton, defaultDefinedSubmitButton = configuration.submitButton, // Action Notification defaultDefinedToastSettings = configuration.toast, // Local // Props Builder defaultPropsBuilder = configuration.defaultProps, overridePropsBuilder = configuration.overrideProps, // Schema and Form extendValidation = configuration.extendValidation, parseDataFn = configuration.parseData, yupSchemaBuilder = configuration.schema, stripUnknown = configuration.stripUnknown; // ---- // Check multiple displayName in development mode only // ---- assertUniqueComponentName(defaultDefinedDisplayName, 'form'); // ---- // Define the Built Component // ---- var Form = function (userDefinedProps) { // ---- // Extract useful props from userDefinedProps // ---- var forcedUserDefinedEditingMode = userDefinedProps.isEditing; // ---- // Computed the isEditing props // ---- var couldBeEditing = React__namespace.useMemo( function () { return ( typeof userDefinedProps.defaultValues === 'object' && userDefinedProps.defaultValues !== null && !Array.isArray(userDefinedProps.defaultValues) && !!Object.keys(userDefinedProps.defaultValues).length ); }, [userDefinedProps.defaultValues] ); var isEditing = typeof forcedUserDefinedEditingMode === 'boolean' ? forcedUserDefinedEditingMode : couldBeEditing; // ---- // Build Component Props // ---- /** Compute default props */ var defaultProps = typeof defaultPropsBuilder === 'function' ? defaultPropsBuilder( _tslib.__assign(_tslib.__assign({}, userDefinedProps), { isEditing: isEditing, }) ) : defaultPropsBuilder; /** Compute override props */ var overrideProps = typeof overridePropsBuilder === 'function' ? overridePropsBuilder( _tslib.__assign( _tslib.__assign( _tslib.__assign({}, defaultProps), userDefinedProps ), { isEditing: isEditing } ) ) : overridePropsBuilder; /** Merge all props into a single props object */ var props = _tslib.__assign( _tslib.__assign(_tslib.__assign({}, defaultProps), userDefinedProps), overrideProps ); // ---- // Deconstruct Props // ---- var // Buttons userDefinedCancelButton = props.cancelButton, userDefinedSubmitButton = props.submitButton, // Modal Props renderAsModal = props.modal, userDefinedModalProps = props.modalProps, // Form Props userDefinedDefaultValues = props.defaultValues; // ---- // Hooks and State Definition // ---- var _a = // eslint-disable-next-line max-len useActionBuilder(configuration, props), actionHelpers = _a.actionHelpers, couldRenderActionButton = _a.couldRenderActionButton, open = _a.open, handleModalOpen = _a.handleModalOpen, handleModalClose = _a.handleModalClose, trigger = _a.trigger, onCancel = _a.onCancel, onCompleted = _a.onCompleted, onSubmit = _a.onSubmit, onSubmitError = _a.onSubmitError; var notify = useActionNotifications(actionHelpers.toast, { onCanceled: defaultDefinedToastSettings === null || defaultDefinedToastSettings === void 0 ? void 0 : defaultDefinedToastSettings.onCanceled, onError: defaultDefinedToastSettings === null || defaultDefinedToastSettings === void 0 ? void 0 : defaultDefinedToastSettings.onError, onSubmitted: isEditing ? defaultDefinedToastSettings === null || defaultDefinedToastSettings === void 0 ? void 0 : defaultDefinedToastSettings.onEditingSubmit : defaultDefinedToastSettings === null || defaultDefinedToastSettings === void 0 ? void 0 : defaultDefinedToastSettings.onCreatingSubmit, }); // ---- // Build Schema and initial defaultValues // ---- var _b = _tslib.__read( React__namespace.useState(function () { return typeof yupSchemaBuilder === 'function' ? yupSchemaBuilder( _tslib.__assign(_tslib.__assign({}, props), { isEditing: isEditing, }) ) : yupSchemaBuilder; }), 1 ), schema = _b[0]; /** Default values are computed once only */ var defaultValues = React__namespace.useMemo( function () { /** Editing mode will clone default values to loose object reference while editing data */ if ((isEditing && couldBeEditing) || couldBeEditing) { /** Clone data */ var clonedData = dataCloner(userDefinedDefaultValues); /** Use the parse function if exists */ var parsedData = typeof parseDataFn === 'function' ? parseDataFn( clonedData, _tslib.__assign(_tslib.__assign({}, props), { isEditing: isEditing, }) ) : clonedData; /** Return casted data using yup schema */ return stripUnknown ? schema.noUnknown().cast(parsedData) : schema.cast(parsedData); } /** Else, if form is not in editing mode, build a default object starting from yup schema */ return defaultValuesFromYupSchema(schema); }, // Heads Up // Props always change, then, default values can't change every render // component props are stripped from useMemo dependencies // eslint-disable-next-line react-hooks/exhaustive-deps [couldBeEditing, isEditing, schema, userDefinedDefaultValues] ); // ---- // Utilities // ---- var isFormValid = React__namespace.useCallback( function (data, helpers) { return _tslib.__awaiter(_this, void 0, void 0, function () { var error_1, _a, errorMessage, errorPath; return _tslib.__generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, , 3]); /** Await form validation */ return [4 /*yield*/, schema.validate(data)]; case 1: /** Await form validation */ _b.sent(); /** Return form is valid */ return [2 /*return*/, true]; case 2: error_1 = _b.sent(); (_a = error_1), (errorMessage = _a.message), (errorPath = _a.path); /** Set error on form */ helpers.setError( errorPath, { message: errorMessage }, { shouldFocus: true } ); /** Return form is invalid */ return [2 /*return*/, false]; case 3: return [2 /*return*/]; } }); }); }, [schema] ); // ---- // Handlers // ---- var handleSubmit = function (data, helpers) { return _tslib.__awaiter(_this, void 0, void 0, function () { var formActionHelpers, isValid, dataToSend, result, error_2, catchFunctionError_1; return _tslib.__generator(this, function (_a) { switch (_a.label) { case 0: formActionHelpers = _tslib.__assign( _tslib.__assign({}, actionHelpers), { form: helpers } ); if (!extendValidation) return [3 /*break*/, 2]; return [4 /*yield*/, isFormValid(data, helpers)]; case 1: isValid = _a.sent(); if (!isValid) { return [2 /*return*/, undefined]; } _a.label = 2; case 2: _a.trys.push([2, 7, , 12]); dataToSend = stripUnknown ? schema.noUnknown().cast(data) : schema.cast(data); result = void 0; if (!(typeof onSubmit === 'function')) return [3 /*break*/, 4]; return [ 4 /*yield*/, onSubmit( dataToSend, formActionHelpers, _tslib.__assign(_tslib.__assign({}, props), { isEditing: isEditing, }) ), ]; case 3: /** Await the result */ result = _a.sent(); _a.label = 4; case 4: if (!(typeof onCompleted === 'function')) return [3 /*break*/, 6]; /** Await function completion */ return [ 4 /*yield*/, onCompleted( result, data, formActionHelpers, _tslib.__assign(_tslib.__assign({}, props), { isEditing: isEditing, }) ), ]; case 5: /** Await function completion */ _a.sent(); _a.label = 6; case 6: /** Raise the Submitted Notification */ notify.raiseOnSubmitted(); /** If has been rendered as modal, close it */ if (renderAsModal) { handleModalClose( null, _tslib.__assign({}, userDefinedModalProps) ); } return [2 /*return*/, result]; case 7: error_2 = _a.sent(); /** Raise the onError notification */ notify.raiseOnError(error_2); if (!(typeof onSubmitError === 'function')) return [3 /*break*/, 11]; _a.label = 8; case 8: _a.trys.push([8, 10, , 11]); /** Await catch error function */ return [ 4 /*yield*/, onSubmitError( error_2, data, formActionHelpers, _tslib.__assign(_tslib.__assign({}, props), { isEditing: isEditing, }) ), ]; case 9: /** Await catch error function */ _a.sent(); return [3 /*break*/, 11]; case 10: catchFunctionError_1 = _a.sent(); /** Log error in development mode only */ if (process.env.NODE_ENV === 'development') { global.console.warn( '[ @appbuckets/react-ui-smart-components ] : an error occurred on onSubmitError handler.', catchFunctionError_1 ); } return [3 /*break*/, 11]; case 11: return [2 /*return*/, undefined]; case 12: return [2 /*return*/]; } }); }); }; var handleCancel = function (data, helpers) { return _tslib.__awaiter(_this, void 0, void 0, function () { var formActionHelpers, error_3; return _tslib.__generator(this, function (_a) { switch (_a.label) { case 0: formActionHelpers = _tslib.__assign( _tslib.__assign({}, actionHelpers), { form: helpers } ); _a.label = 1; case 1: _a.trys.push([1, 4, , 5]); if (!(typeof onCancel === 'function')) return [3 /*break*/, 3]; /** Fire the onCancel Handler */ return [ 4 /*yield*/, onCancel( formActionHelpers, _tslib.__assign(_tslib.__assign({}, props), { isEditing: isEditing, }) ), ]; case 2: /** Fire the onCancel Handler */ _a.sent(); _a.label = 3; case 3: /** Close the Modal is Confirm is rendered as it */ if (renderAsModal) { handleModalClose(null, userDefinedModalProps || {}); } /** Raise the onCanceled Notification */ notify.raiseOnCanceled(); return [3 /*break*/, 5]; case 4: error_3 = _a.sent(); /** Log error in development mode only */ if (process.env.NODE_ENV === 'development') { global.console.warn( '[ @appbuckets/react-ui-smart-components ] : an error occurred on onCancel handler.', error_3 ); } return [3 /*break*/, 5]; case 5: return [2 /*return*/]; } }); }); }; // ---- // Build the Form Element // ---- var formElement = React__namespace.createElement( HookedForm__default['default'], { resetOnCancel: true, actionsWrapper: renderAsModal ? Modal__default['default'].Actions : 'div', contentWrapper: renderAsModal ? Modal__default['default'].Content : 'div', submitButton: couldRenderActionButton( userDefinedSubmitButton, defaultDefinedSubmitButton ) ? userDefinedSubmitButton || defaultDefinedSubmitButton : null, cancelButton: couldRenderActionButton( userDefinedCancelButton, defaultDefinedCancelButton ) ? userDefinedCancelButton || defaultDefinedCancelButton : null, defaultValues: defaultValues, restoreDefaultValuesIfChanged: renderAsModal ? !open : false, onSubmit: handleSubmit, onCancel: handleCancel, resolver: yup.yupResolver(schema), }, React__namespace.createElement( FormBuiltProvider.FormBuiltProvider, { value: { isEditing: isEditing } }, actionHelpers.error && React__namespace.createElement( QuerySuspenseError, _tslib.__assign({}, actionHelpers.error) ), FormContent && React__namespace.createElement( FormContent, _tslib.__assign({}, props, { isEditing: isEditing }) ) ) ); // ---- // Render Component as plain element // ---- if (!renderAsModal) { return formElement || null; } // ---- // Render Component as modal element // ---- return React__namespace.createElement( Modal__default['default'], _tslib.__assign({}, userDefinedModalProps, { open: open, onOpen: handleModalOpen, onClose: handleModalClose, trigger: trigger, }), formElement ); }; // ---- // Set the Display Name // ---- Form.displayName = defaultDefinedDisplayName; // ---- // Return the Component // ---- return Form; } module.exports = buildFormAction;