canner
Version:
Build CMS in few lines of code for different data sources
434 lines (356 loc) • 13.7 kB
JavaScript
;
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.findNode = findNode;
exports.isToList = isToList;
exports.default = void 0;
require("antd/lib/alert/style");
var _alert = _interopRequireDefault(require("antd/lib/alert"));
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var React = _interopRequireWildcard(require("react"));
var _reactLoadable = _interopRequireDefault(require("react-loadable"));
var _lodash = require("lodash");
var _cannerRefId = _interopRequireDefault(require("canner-ref-id"));
var _cannerLayouts = _interopRequireDefault(require("canner-layouts"));
var _antd = require("antd");
var _cannerHelpers = require("canner-helpers");
var _hocs = _interopRequireDefault(require("../hocs"));
var _reactContentLoader = require("react-content-loader");
/**
* Genertaor is a Component that renders the components, layouts
* with the a given componentTree which is created by qa-compiler
*
* First Step, prerender the tree in constructor, this action will add a
* React Component with all hocs it needs in every node
*
* Second Step, take the node.component to render it, and give the component
* some props it maybe needs such as renderChildren
*/
var Generator =
/*#__PURE__*/
function (_React$Component) {
(0, _inherits2.default)(Generator, _React$Component);
function Generator(_props) {
var _this;
(0, _classCallCheck2.default)(this, Generator);
_this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(Generator).call(this, _props));
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "idNodeMap", {});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "renderNode", function (node, props, index) {
// take the node.component to render it, and give the component
// some props it maybe needs such as renderChildren
// eslint-disable-next-line no-unused-vars
if (!node) {
throw new Error("Unexpected Error: Want to render a undefined node with refId '".concat(props.refId.toString(), "'"));
}
var component = node.component,
restNodeData = (0, _objectWithoutProperties2.default)(node, ["component"]);
var _this$props = _this.props,
_this$props$routerPar = _this$props.routerParams,
routerParams = _this$props$routerPar === void 0 ? {} : _this$props$routerPar,
goTo = _this$props.goTo,
routes = _this$props.routes,
imageStorages = _this$props.imageStorages,
fileStorages = _this$props.fileStorages,
onDeploy = _this$props.onDeploy,
removeOnDeploy = _this$props.removeOnDeploy,
hideButtons = _this$props.hideButtons,
schema = _this$props.schema;
var renderChildren = function renderChildren(props) {
return _this.renderChildren(node, props);
};
if (node.hidden || props.hidden) {
return null;
}
if (component) {
var contextValue = {
renderChildren: renderChildren,
routes: routes,
refId: props.refId
};
return React.createElement("div", {
"data-testid": node.path
}, React.createElement(_cannerHelpers.Context.Provider, {
key: restNodeData.keyName ? "".concat(props.refId.toString(), "/").concat(restNodeData.keyName) : index,
value: contextValue
}, React.createElement(node.component, (0, _extends2.default)({
hideButtons: hideButtons,
routes: routes,
imageStorage: (imageStorages || {})[routes[0]],
fileStorage: (fileStorages || {})[routes[0]],
renderChildren: function renderChildren(props) {
return _this.renderChildren(node, props);
},
renderComponent: _this.renderComponent,
routerParams: routerParams,
onDeploy: onDeploy,
removeOnDeploy: removeOnDeploy,
schema: schema,
goTo: goTo
}, restNodeData, props))));
}
return null;
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "renderComponent", function (refId, props) {
var cacheTree = _this.state.cacheTree;
var componentPathArr = refId.getPathArr().filter(function (path) {
return isNaN(Number(path));
});
var componentPath = componentPathArr.join('/');
var node = _this.idNodeMap[componentPath];
var entryKey = componentPathArr[0];
if (!node) {
var lastPath = componentPathArr.slice(1);
if (lastPath.length === 0) {
node = cacheTree[entryKey];
} else {
node = findNode(componentPathArr.slice(), cacheTree[entryKey]);
}
_this.idNodeMap[componentPath] = node;
}
if (!node) {
throw new Error("Can't find the node at refId ".concat(refId.toString()));
}
return _this.renderNode(node, (0, _objectSpread2.default)({
refId: refId.remove(1),
keyName: refId.getPathArr().slice(-1)[0]
}, props), 0);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "renderChildren", function (node, props) {
// just get the props and call renderNode
// this method is called by components themselves
var children = node.children;
if (children) {
return children.map(function (child, index) {
var childProps = typeof props === 'function' ? props(child) : props;
var refId = childProps.refId;
if ((0, _lodash.isUndefined)(refId)) {
throw new Error("refId is required for renderChildren, please check node '".concat(node.keyName || '', "'"));
}
if (childProps.hidden) {
return null;
}
if (childProps.mergeNode) {
// mutate node
childProps.mergeNode(node);
}
return _this.renderNode(child, childProps, index);
});
}
return null;
});
var componentTree = _props.componentTree;
_this.state = {
cacheTree: genCacheTree(componentTree),
error: null,
errorInfo: {}
};
return _this;
}
(0, _createClass2.default)(Generator, [{
key: "componentDidMount",
value: function componentDidMount() {
var _this$props2 = this.props,
componentTree = _this$props2.componentTree,
routes = _this$props2.routes,
goTo = _this$props2.goTo,
defaultKey = _this$props2.defaultKey;
var activeKey = routes[0];
if (!activeKey) {
activeKey = Object.keys(componentTree)[0];
goTo({
pathname: defaultKey || activeKey
});
}
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
if (this.props.refresh && !(0, _lodash.isEqual)(prevProps.componentTree, this.props.componentTree)) {
this.setState({
cacheTree: genCacheTree(this.props.componentTree)
});
}
if (isToList(prevProps, this.props)) {
this.props.reset && this.props.reset();
}
}
}, {
key: "componentDidCatch",
value: function componentDidCatch(error, errorInfo) {
// Catch errors in any components below and re-render with error message
this.setState({
error: error,
errorInfo: errorInfo
});
}
}, {
key: "render",
value: function render() {
var _this$state = this.state,
cacheTree = _this$state.cacheTree,
error = _this$state.error,
errorInfo = _this$state.errorInfo;
var _this$props3 = this.props,
routes = _this$props3.routes,
routerParams = _this$props3.routerParams;
if (error) {
return errorInfo;
}
if (!routes[0] || !cacheTree[routes[0]]) {
return null;
}
return React.createElement("div", null, this.renderNode(cacheTree[routes[0]], {
refId: new _cannerRefId.default(''),
routes: routes,
routerParams: routerParams
}, 0));
}
}]);
return Generator;
}(React.Component);
exports.default = Generator;
(0, _defineProperty2.default)(Generator, "defaultProps", {
componentTree: {},
layouts: {}
});
function defaultHoc(Component) {
return Component;
}
function isComponent(node) {
return node.nodeType && node.nodeType.startsWith('component');
}
function isLayout(node) {
return node.nodeType && node.nodeType.startsWith('layout');
}
function isFieldset(node) {
return node.packageName === '@canner/antd-object-fieldset';
}
function isPage(node) {
return node.nodeType && node.nodeType.startsWith('page');
}
function isPageRoot(node) {
return node.nodeType === 'page.page.default';
}
function inPage(node) {
return node.pattern.startsWith('page');
}
function Loading(props) {
if (props.error) {
return React.createElement(_alert.default, {
message: "Something went wrong.",
description: props.error,
type: "error",
closable: true
});
} else {
return React.createElement(_reactContentLoader.List, {
style: {
maxWidth: '600px'
}
});
}
}
function createLoadableComponnet(node) {
return (0, _reactLoadable.default)({
loader: function loader() {
return node.loader || Promise.reject("There is no loader in ".concat(node.path));
},
loading: Loading
});
}
function generateComponent(node) {
var component = node.component;
if (isLayout(node)) {
if (!node.component) {
component = _cannerLayouts.default[node.ui];
}
return wrapByHOC(component, node.ui === 'condition' ? ['containerQuery', 'context'] : ['context']);
}
if (isComponent(node)) {
if (isFieldset(node)) {
component = function component() {
return React.createElement(_cannerHelpers.Item, null);
};
} else {
component = createLoadableComponnet(node);
}
return wrapByHOC(component, ['title', 'onDeploy', 'validation', 'deploy', 'request', 'relation', 'query', 'cache', 'route', 'id', 'context', 'errorCatch']);
} else if (isPage(node)) {
if (isPageRoot(node)) {
component = function component() {
return React.createElement(_cannerHelpers.Item, null);
};
} else if (inPage(node)) {
component = createLoadableComponnet(node);
return wrapByHOC(component, ['graphqlQuery']);
} else {
component = createLoadableComponnet(node);
component = wrapByHOC(component, ['title', 'onDeploy', 'validation', 'deploy', 'request', 'relation', 'query', 'cache', 'route', 'id', 'context', 'errorCatch']);
}
}
return component;
}
function wrapByHOC(component, hocNames) {
// find hocs and wrap the component
while (hocNames.length) {
var hocName = hocNames.shift();
var hoc = (0, _lodash.get)(_hocs.default, hocName, defaultHoc);
component = hoc(component);
}
return component;
} // wrap the plugin with hoc if it has
function prerender(node) {
// add a field `component` in every node.
// it's a React Component with all hocs it needs in every node
var copyNode = (0, _objectSpread2.default)({}, node);
var component = generateComponent(node);
if (!component) {
throw new Error("invalid node, name: ".concat(copyNode.keyName, ", nodeType: ").concat(copyNode.nodeType));
}
copyNode.component = component;
if (copyNode.children) {
copyNode.children = copyNode.children.map(function (child) {
return prerender(child);
});
}
return copyNode;
}
function genCacheTree(tree) {
return (0, _lodash.mapValues)(tree, function (branch) {
return prerender(branch);
});
}
function findNode(pathArr, node) {
if (isComponent(node) && node.keyName === pathArr[0]) {
pathArr = pathArr.slice(1);
if (!pathArr.length) {
return node;
}
}
if (node.children) {
return node.children.map(function (child) {
return findNode(pathArr, child);
}).find(function (node) {
return !!node;
});
}
}
function isToList(preProps, props) {
var preRoutes = JSON.stringify(preProps.routes);
var routes = JSON.stringify(props.routes);
var preOperator = preProps.routerParams.operator;
var operator = props.routerParams.operator;
return preRoutes === routes && preOperator === 'create' && operator === 'update';
}