@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
JavaScript
'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;