@talend/react-cmf
Version:
A framework built on top of best react libraries
395 lines (388 loc) • 14.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = cmfConnect;
exports.getComponentId = getComponentId;
exports.getComponentName = getComponentName;
exports.getDispatchToProps = getDispatchToProps;
exports.getMergeProps = getMergeProps;
exports.getStateToProps = getStateToProps;
var _propTypes = _interopRequireDefault(require("prop-types"));
var _react = require("react");
var _hoistNonReactStatics = _interopRequireDefault(require("hoist-non-react-statics"));
var _reactImmutableProptypes = _interopRequireDefault(require("react-immutable-proptypes"));
var _reactRedux = require("react-redux");
var _utils = require("@talend/utils");
var _actions = _interopRequireDefault(require("./actions"));
var _actionCreator = _interopRequireDefault(require("./actionCreator"));
var _component = _interopRequireDefault(require("./component"));
var _constant = _interopRequireDefault(require("./constant"));
var _expression = _interopRequireDefault(require("./expression"));
var _onEvent = _interopRequireDefault(require("./onEvent"));
var _componentState = require("./componentState");
var _settings = require("./settings");
var _omit = _interopRequireDefault(require("./omit"));
var _RegistryProvider = require("./RegistryProvider");
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } /**
* This module connect your component in the CMF environment.
* @module react-cmf/lib/cmfConnect
* @example
import { cmfConnect } from '@talend/react-cmf';
function MyComponent(props) {
const onClick = (event) => {
props.dispatchActionCreator('myaction', event, { props: props });
};
return <button onClick={onClick}>Edit {props.foo.name}</button>;
}
function mapStateToProps(state) {
return {
foo: state.cmf.collection.get('foo', { name: 'world' }),
};
}
export default cmfConnect({
mapStateToProps,
});
*/
function getComponentName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
function getComponentId(componentId, props) {
if (typeof componentId === 'function') {
return componentId(props) || 'default';
} else if (typeof componentId === 'string') {
return componentId;
} else if (props.componentId) {
return props.componentId;
}
return 'default';
}
function getStateToProps({
defaultProps,
componentId,
ownProps,
state,
mapStateToProps,
WrappedComponent
}) {
const props = {
...defaultProps
};
const cmfProps = (0, _componentState.getStateProps)(state, getComponentName(WrappedComponent), getComponentId(componentId, ownProps));
_extends(props, cmfProps);
const viewProps = (0, _settings.mapStateToViewProps)(state, ownProps, getComponentName(WrappedComponent), getComponentId(componentId, ownProps));
_extends(props, viewProps);
let userProps = {};
if (mapStateToProps) {
userProps = mapStateToProps(state, {
...ownProps,
...props
}, cmfProps);
}
_extends(props, userProps);
_extends(props, _expression.default.mapStateToProps(state, {
...ownProps,
...props
}));
return props;
}
function getDispatchToProps({
defaultState,
dispatch,
componentId,
mapDispatchToProps,
ownProps,
WrappedComponent
}) {
const cmfProps = (0, _componentState.getStateAccessors)(dispatch, getComponentName(WrappedComponent), getComponentId(componentId, ownProps), defaultState);
cmfProps.dispatch = dispatch;
cmfProps.getComponent = _component.default.get;
cmfProps.dispatchActionCreator = (actionId, event, data, context) => {
dispatch(_actionCreator.default.get(context, actionId)(event, data, context));
};
let userProps = {};
if (mapDispatchToProps) {
if (process.env.NODE_ENV === 'development') {
// eslint-disable-next-line no-console
console.warn(`DEPRECATION WARNING: mapDispatchToProps will be removed from cmfConnect.
Please use the injectedProps dispatchActionCreator or dispatch`);
}
userProps = mapDispatchToProps(dispatch, ownProps, cmfProps);
}
return {
...cmfProps,
...userProps
};
}
/**
* Internal: you should not have to use this
* return the merged props which cleanup expression props
* call mergeProps if exists after the cleanup
* @param {object} options { mergeProps, stateProps, dispatchProps, ownProps }
*/
function getMergeProps({
mergeProps,
stateProps,
dispatchProps,
ownProps
}) {
if (mergeProps) {
return mergeProps(_expression.default.mergeProps(stateProps), _expression.default.mergeProps(dispatchProps), _expression.default.mergeProps(ownProps));
}
return {
..._expression.default.mergeProps(ownProps),
..._expression.default.mergeProps(dispatchProps),
..._expression.default.mergeProps(stateProps)
};
}
/**
* this function wrap your component to inject CMF props
* @example
* The following props are injected:
* - props.state
* - props.setState
* - props.initState (you should never have to call it your self)
* - dispatch(action)
* - dispatchActionCreator(id, event, data, [context])
*
* support for the following props
* - initialState (called by props.initState)
* - didMountActionCreator (id or array of id)
* - willUnMountActionCreator (id or array of id)
* - componentId (or will use uuid)
* - keepComponentState (boolean, overrides the keepComponentState defined in container)
* - didMountActionCreator (string called as action creator in didMount)
* - view (string to inject the settings as props with ref support)
* - whateverExpression (will inject `whatever` props and will remove it)
* @example
* options has the following shape:
{
componentId, // string or function(props) to compute the id in the store
defaultState, // the default state when the component is mount
keepComponent, // boolean, when the component is unmount, to keep it's state in redux store
mapStateToProps, // function(state, ownProps) that should return the props (same as redux)
mapDispatchToProps, // same as redux connect arg, you should use dispatchActionCreator instead
mergeProps, // same as redux connect
}
* @param {object} options Option objects to configure the redux connect
* @return {ReactComponent}
*/
function cmfConnect({
componentId,
defaultState,
defaultProps,
keepComponentState,
mapStateToProps,
mapDispatchToProps,
mergeProps,
omitCMFProps = true,
withComponentRegistry = false,
withDispatch = false,
withDispatchActionCreator = false,
withComponentId = false,
...rest
} = {}) {
const propsToOmit = [];
if (omitCMFProps) {
if (!defaultState) {
propsToOmit.push(..._constant.default.INJECTED_STATE_PROPS);
}
if (!withComponentRegistry) {
propsToOmit.push('getComponent');
}
if (!withComponentId) {
propsToOmit.push('componentId');
}
if (!withDispatch) {
propsToOmit.push('dispatch');
}
if (!withDispatchActionCreator) {
propsToOmit.push('dispatchActionCreator');
}
}
let displayNameWarning = true;
return function wrapWithCMF(WrappedComponent) {
if (!WrappedComponent.displayName && displayNameWarning) {
displayNameWarning = false;
// eslint-disable-next-line no-console
console.warn(`${WrappedComponent.name} has no displayName. Please read https://jira.talendforge.org/browse/TUI-302`);
}
function getState(state, id = 'default') {
return state.cmf.components.getIn([getComponentName(WrappedComponent), id], defaultState);
}
function getSetStateAction(state, id, type) {
return {
type: type || `${getComponentName(WrappedComponent)}.setState`,
cmf: {
componentState: _actions.default.components.mergeState(getComponentName(WrappedComponent), id, state)
}
};
}
function CMFContainer(props, ref) {
const [instanceId] = (0, _react.useState)((0, _utils.randomUUID)());
const registry = (0, _react.useContext)(_RegistryProvider.RegistryContext);
const store = (0, _reactRedux.useStore)();
function dispatchActionCreator(actionCreatorId, event, data, extraContext) {
const extendedContext = {
registry,
store,
...extraContext
};
props.dispatchActionCreator(actionCreatorId, event, data, extendedContext);
}
(0, _react.useEffect)(() => {
(0, _componentState.initState)(props);
if (props.saga) {
dispatchActionCreator('cmf.saga.start', {
type: 'DID_MOUNT',
componentId: instanceId
}, {
...props,
// DEPRECATED
componentId: getComponentId(componentId, props)
});
}
if (props.didMountActionCreator) {
dispatchActionCreator(props.didMountActionCreator, null, props);
}
return () => {
if (props.willUnmountActionCreator) {
dispatchActionCreator(props.willUnmountActionCreator, null, props);
}
// if the props.keepComponentState is present we have to stick to it
if (props.keepComponentState === false || props.keepComponentState === undefined && !keepComponentState) {
props.deleteState(props.initialState);
}
if (props.saga) {
dispatchActionCreator('cmf.saga.stop', {
type: 'WILL_UNMOUNT',
componentId: instanceId
}, props);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
function getOnEventProps() {
return Object.keys(props).reduce((acc, key) => {
// TODO check how to replace the this
_onEvent.default.addOnEventSupport(_onEvent.default.DISPATCH, {
props
}, acc, key);
_onEvent.default.addOnEventSupport(_onEvent.default.ACTION_CREATOR, {
props
}, acc, key);
_onEvent.default.addOnEventSupport(_onEvent.default.SETSTATE, {
props
}, acc, key);
return acc;
}, {
toOmit: [],
dispatchActionCreator
});
}
if (props.renderIf === false) {
return null;
}
const {
toOmit,
spreadCMFState,
...handlers
} = getOnEventProps();
// remove all internal props already used by the container
delete handlers.dispatchActionCreator;
toOmit.push(..._constant.default.CMF_PROPS, ...propsToOmit);
if (props.omitRouterProps) {
toOmit.push('omitRouterProps', ..._constant.default.INJECTED_ROUTER_PROPS);
}
let spreadedState = {};
if ((spreadCMFState || props.spreadCMFState) && props.state) {
spreadedState = props.state.toJS();
}
const newProps = {
...(0, _omit.default)(props, toOmit),
...handlers,
...spreadedState
};
if (newProps.dispatchActionCreator && toOmit.indexOf('dispatchActionCreator') === -1) {
// override to inject CMFContainer context
newProps.dispatchActionCreator = dispatchActionCreator;
}
if (!newProps.state && defaultState && toOmit.indexOf('state') === -1) {
newProps.state = defaultState;
}
if (rest.forwardRef) {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(WrappedComponent, {
...newProps,
ref: ref
});
}
return /*#__PURE__*/(0, _jsxRuntime.jsx)(WrappedComponent, {
...newProps
});
}
let CMFWithRef = (0, _hoistNonReactStatics.default)(CMFContainer, WrappedComponent);
CMFContainer.displayName = `CMF(${getComponentName(WrappedComponent)})`;
CMFContainer.propTypes = {
...cmfConnect.propTypes
};
CMFContainer.WrappedComponent = WrappedComponent;
CMFContainer.getState = getState;
// eslint-disable-next-line @typescript-eslint/default-param-last
CMFContainer.setStateAction = function setStateAction(state, id = 'default', type) {
if (typeof state !== 'function') {
return getSetStateAction(state, id, type);
}
return (_, getReduxState) => getSetStateAction(state(getState(getReduxState(), id)), id, type);
};
if (rest.forwardRef) {
CMFWithRef = /*#__PURE__*/(0, _react.forwardRef)(CMFWithRef);
CMFWithRef.displayName = `ForwardRef(${CMFContainer.displayName})`;
CMFWithRef.WrappedComponent = CMFContainer.WrappedComponent;
CMFWithRef.getState = CMFContainer.getState;
CMFWithRef.setStateAction = CMFContainer.setStateAction;
}
const Connected = (0, _reactRedux.connect)((state, ownProps) => getStateToProps({
componentId,
defaultProps,
defaultState,
ownProps,
state,
mapStateToProps,
WrappedComponent
}), (dispatch, ownProps) => getDispatchToProps({
defaultState,
dispatch,
componentId,
mapDispatchToProps,
ownProps,
WrappedComponent
}), (stateProps, dispatchProps, ownProps) => getMergeProps({
mergeProps,
stateProps,
dispatchProps,
ownProps
}), {
...rest
})(CMFWithRef);
Connected.CMFContainer = CMFContainer;
return Connected;
};
}
cmfConnect.INJECTED_PROPS = _constant.default.INJECTED_PROPS;
cmfConnect.INJECTED_STATE_PROPS = _constant.default.INJECTED_STATE_PROPS;
cmfConnect.INJECTED_ROUTER_PROPS = _constant.default.INJECTED_ROUTER_PROPS;
cmfConnect.ALL_INJECTED_PROPS = _constant.default.INJECTED_PROPS.concat(['getComponent', 'componentId']);
cmfConnect.omit = _omit.default;
cmfConnect.omitAllProps = props => cmfConnect.omit(props, cmfConnect.ALL_INJECTED_PROPS);
cmfConnect.propTypes = {
state: _reactImmutableProptypes.default.map,
initialState: _propTypes.default.oneOfType([_reactImmutableProptypes.default.map, _propTypes.default.object]),
getComponent: _propTypes.default.func,
setState: _propTypes.default.func,
initState: _propTypes.default.func,
dispatchActionCreator: _propTypes.default.func,
dispatch: _propTypes.default.func
};
//# sourceMappingURL=cmfConnect.js.map