zent
Version:
一套前端设计语言和基于React的实现
1,066 lines (813 loc) • 32.2 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
var _defineProperty2 = require('babel-runtime/helpers/defineProperty');
var _defineProperty3 = _interopRequireDefault(_defineProperty2);
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _reactDom = require('react-dom');
var _alert = require('../alert');
var _alert2 = _interopRequireDefault(_alert);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _classnames = require('classnames');
var _classnames2 = _interopRequireDefault(_classnames);
var _assign2 = require('lodash/assign');
var _assign3 = _interopRequireDefault(_assign2);
var _find = require('lodash/find');
var _find2 = _interopRequireDefault(_find);
var _findIndex2 = require('lodash/findIndex');
var _findIndex3 = _interopRequireDefault(_findIndex2);
var _isEmpty = require('lodash/isEmpty');
var _isEmpty2 = _interopRequireDefault(_isEmpty);
var _isUndefined = require('lodash/isUndefined');
var _isUndefined2 = _interopRequireDefault(_isUndefined);
var _defaultTo = require('lodash/defaultTo');
var _defaultTo2 = _interopRequireDefault(_defaultTo);
var _defer = require('lodash/defer');
var _defer2 = _interopRequireDefault(_defer);
var _DesignPreview = require('./preview/DesignPreview');
var _DesignPreview2 = _interopRequireDefault(_DesignPreview);
var _uuid = require('./utils/uuid');
var _uuid2 = _interopRequireDefault(_uuid);
var _designType = require('./utils/design-type');
var _storage = require('./utils/storage');
var storage = _interopRequireWildcard(_storage);
var _InstanceCountMap = require('./utils/InstanceCountMap');
var _InstanceCountMap2 = _interopRequireDefault(_InstanceCountMap);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var UUID_KEY = '__zent-design-uuid__'; /* eslint-disable no-script-url */
/**
* 设计文档
*
* 预览
* `DesignPreview` 组件是整个预览块的包裹层,负责渲染左侧预览的框架。`DesignPreview` 和 `config`
* 子组件是相关的,`config` 组件是知道 `DesignPreview` 的存在的;而 `DesignPreview` 的渲染是
* 根据 `config` 生成的数据进行的。
* ⚠️注意:`config` 自身有相应的负责渲染预览的模块,这个和 `DesignPreview` 不冲突,可以理解成
* `config` 可以控制一些预览界面的全局样式。
* 预览界面中按模块分成很多区域,每个区域是一个 `DesignPreviewItem`,默认的 `DesignPreviewItem`
* 实现可以由外部覆盖。负责每个区域的事件交互的是另一个组件 `DesignPreviewController`,这个组件
* 负责处理添加、删除、编辑、选中以及拖拽操作,`DesignPreviewController` 的实现也是可以由外部覆盖的。
* ⚠️注意:重写的时候所有交互都需要再这个组件里面处理。`DesignPreviewController` 内部会渲染该区域
* 对应组件的预览模块,预览模块有两个参数:`value` 和 `design`。`value` 是当前的值,`design` 是
* `Design` 组件提供的一些操作,一般用不到。
* 编辑
* `DesignEditorItem` 是每个区域对应的编辑区域,这个区域的显示隐藏由 `Design` 控制。`DesignEditorItem`
* 可以由外部覆盖重写。
* `DesignEditorAddComponent` 这个组件负责枚举所有**可以添加的组件**,暂不支持由外部自定义组件实现。
* `DesignEditor` 是所有编辑组件的基类,这个类提供了一些常用的方法(例如 `onChange` 事件的处理函数),
* 在子类里面可以直接使用。
*/
var CACHE_KEY = '__zent-design-cache-storage__';
var hasValidateError = function hasValidateError(v) {
return !(0, _isEmpty2['default'])(v[Object.keys(v)[0]]);
};
var Design = function (_ref) {
(0, _inherits3['default'])(Design, _ref);
function Design(props) {
(0, _classCallCheck3['default'])(this, Design);
var _this = (0, _possibleConstructorReturn3['default'])(this, (Design.__proto__ || Object.getPrototypeOf(Design)).call(this, props));
_initialiseProps.call(_this);
var value = props.value,
defaultSelectedIndex = props.defaultSelectedIndex;
_this.validateCacheProps(props);
tagValuesWithUUID(value);
var safeValueIndex = getSafeSelectedValueIndex(defaultSelectedIndex, value);
var selectedValue = value[safeValueIndex];
_this.state = {
// 当前选中的组件对应的 UUID
selectedUUID: _this.getUUIDFromValue(selectedValue),
// 每个组件当前已经添加的个数
componentInstanceCount: makeInstanceCountMapFromValue(props.value, props.components),
// 是否显示添加组件的浮层
showAddComponentOverlay: false,
// 可添加的组件列表
appendableComponents: [],
// 当前所有组件的 validation 信息
// key 是 value 的 UUID
validations: {},
// 是否强制显示错误
showError: false,
// 是否显示从缓存中恢复的提示
showRestoreFromCache: false,
// 当 preview 很长时,为了对齐 preview 底部需要的额外空间
bottomGap: 0
};
return _this;
}
(0, _createClass3['default'])(Design, [{
key: 'render',
value: function render() {
var _props = this.props,
className = _props.className,
prefix = _props.prefix,
preview = _props.preview,
cacheRestoreMessage = _props.cacheRestoreMessage,
children = _props.children;
var _state = this.state,
showRestoreFromCache = _state.showRestoreFromCache,
bottomGap = _state.bottomGap;
var cls = (0, _classnames2['default'])(prefix + '-design', className);
return _react2['default'].createElement(
'div',
{ className: cls, style: { paddingBottom: bottomGap } },
showRestoreFromCache && _react2['default'].createElement(
_alert2['default'],
{
className: prefix + '-design__restore-cache-alert',
closable: true,
onClose: this.onRestoreCacheAlertClose,
type: 'warning'
},
cacheRestoreMessage,
_react2['default'].createElement(
'a',
{
className: prefix + '-design__restore-cache-alert-use',
onClick: this.restoreCache,
href: 'javascript:void(0);'
},
'\u4F7F\u7528'
)
),
this.renderPreview(preview),
children
);
}
}, {
key: 'componentWillMount',
value: function componentWillMount() {
this.cacheAppendableComponents(this.props.components);
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
this.setupBeforeUnloadHook();
this.checkCache();
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate() {
this.setupBeforeUnloadHook();
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
this.uninstallBeforeUnloadHook();
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
this.validateCacheProps(nextProps);
var shouldUpdateInstanceCountMap = false;
if (nextProps.value !== this.props.value) {
tagValuesWithUUID(nextProps.value);
shouldUpdateInstanceCountMap = true;
}
if (nextProps.components !== this.props.components) {
this.cacheAppendableComponents(nextProps.components);
shouldUpdateInstanceCountMap = true;
}
// 如果当前没有选中的并且 value 或者 defaultSelectedIndex 改变的话
// 重新尝试设置默认值
if (!this.hasSelected() && (nextProps.defaultSelectedIndex !== this.props.defaultSelectedIndex || nextProps.value !== this.props.value)) {
var value = nextProps.value,
defaultSelectedIndex = nextProps.defaultSelectedIndex;
this.selectByIndex(defaultSelectedIndex, value);
}
if (shouldUpdateInstanceCountMap) {
this.setState({
componentInstanceCount: makeInstanceCountMapFromValue(nextProps.value, nextProps.components)
});
}
}
}, {
key: 'cacheAppendableComponents',
value: function cacheAppendableComponents(components) {
this.setState({
appendableComponents: components.filter(function (c) {
return c.appendable === undefined || c.appendable;
})
});
}
}, {
key: 'renderPreview',
value: function renderPreview(preview) {
var _props2 = this.props,
components = _props2.components,
prefix = _props2.prefix,
value = _props2.value,
disabled = _props2.disabled,
globalConfig = _props2.globalConfig;
var _state2 = this.state,
selectedUUID = _state2.selectedUUID,
appendableComponents = _state2.appendableComponents,
showAddComponentOverlay = _state2.showAddComponentOverlay,
validations = _state2.validations,
showError = _state2.showError,
componentInstanceCount = _state2.componentInstanceCount;
return _react2['default'].createElement(preview, (0, _extends3['default'])({
prefix: prefix,
components: components,
value: value,
validations: validations,
showError: showError,
componentInstanceCount: componentInstanceCount,
onComponentValueChange: this.onComponentValueChange,
onAddComponent: this.onAdd,
appendableComponents: appendableComponents,
selectedUUID: selectedUUID,
getUUIDFromValue: this.getUUIDFromValue,
showAddComponentOverlay: showAddComponentOverlay,
onAdd: this.onShowAddComponentOverlay,
onEdit: this.onShowEditComponentOverlay,
onSelect: this.onSelect,
onMove: this.onMove,
onDelete: this.onDelete,
design: this.design,
globalConfig: globalConfig,
disabled: disabled
}, this.getPreviewProps(), {
ref: this.savePreview
}));
}
// 打开右侧添加新组件的弹层
// 编辑一个已有组件
// 选中一个组件
// 添加一个新组件
// 删除一个组件
// 交换两个组件的位置
}, {
key: 'getPreviewProps',
// Injections can be overwritten
value: function getPreviewProps() {}
// 验证所有组件,如果有错误选中并跳转到第一个有错误的组件。
// 如果没有错误,Promise resolve;如果有错误,Promise reject。
// reject 的是个数组,
// [
// { '508516bf-d3e5-40a5-812e-834d3dee1d54': {} },
// { 'c7c72599-2ac5-41bb-9ba0-45e8178ff5a6': { content: '请填写公告内容' } }
// ]
// 保存数据后请调用这个函数通知组件数据已经保存
}, {
key: 'toggleEditOrAdd',
value: function toggleEditOrAdd(component, showAdd) {
var showAddComponentOverlay = this.state.showAddComponentOverlay;
var id = this.getUUIDFromValue(component);
if (this.isSelected(component) && showAddComponentOverlay === showAdd) {
return;
}
this.setState({
selectedUUID: id,
showAddComponentOverlay: showAdd
});
this.adjustHeight();
}
}, {
key: 'getUUIDFromValue',
value: function getUUIDFromValue(value) {
return value && value[UUID_KEY];
}
}, {
key: 'setUUIDForValue',
value: function setUUIDForValue(value, id) {
if (value) {
value[UUID_KEY] = id;
}
return value;
}
}, {
key: 'scrollToPreviewItem',
// 滚动到第一个有错误的组件
value: function scrollToPreviewItem(id) {
if (this.preview) {
var _props3 = this.props,
scrollTopOffset = _props3.scrollTopOffset,
scrollLeftOffset = _props3.scrollLeftOffset;
this.preview.scrollToItem && this.preview.scrollToItem(id, {
top: scrollTopOffset,
left: scrollLeftOffset
});
}
}
// 调整 Design 的高度,因为 editor 是 position: absolute 的,所以需要动态的更新
// 实际并未改变高度,而是设置了margin/padding
}, {
key: 'trackValueChange',
// 调用 onChange 的统一入口,用于处理一些需要知道有没有修改过值的情况
value: function trackValueChange(newValue) {
var writeCache = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var onChange = this.props.onChange;
onChange(newValue);
if (!this._dirty) {
this._dirty = true;
}
if (writeCache) {
this.writeCache(newValue);
}
this.adjustHeight();
}
}, {
key: 'setupBeforeUnloadHook',
value: function setupBeforeUnloadHook() {
var confirmUnsavedLeave = this.props.confirmUnsavedLeave;
if (this._hasBeforeUnloadHook || !confirmUnsavedLeave) {
return;
}
window.addEventListener('beforeunload', this.onBeforeWindowUnload);
this._hasBeforeUnloadHook = true;
}
}, {
key: 'uninstallBeforeUnloadHook',
value: function uninstallBeforeUnloadHook() {
window.removeEventListener('beforeunload', this.onBeforeWindowUnload);
this._hasBeforeUnloadHook = false;
}
}, {
key: 'validateCacheProps',
// 缓存相关的函数
value: function validateCacheProps(props) {
props = props || this.props;
var _props4 = props,
cache = _props4.cache,
cacheId = _props4.cacheId;
if (cache && !cacheId) {
throw new Error('Design: cacheId is required when cache is on');
}
}
}, {
key: 'checkCache',
value: function checkCache() {
var cache = this.props.cache;
if (cache) {
var cachedValue = this.readCache();
if (cachedValue !== storage.NOT_FOUND) {
this.setState({
showRestoreFromCache: true
});
}
}
}
}, {
key: 'readCache',
value: function readCache() {
var cache = this.props.cache;
if (!cache) {
return storage.NOT_FOUND;
}
var cacheId = this.props.cacheId;
return storage.read(CACHE_KEY, cacheId);
}
}, {
key: 'writeCache',
value: function writeCache(value) {
var cache = this.props.cache;
if (!cache) {
return false;
}
var cacheId = this.props.cacheId;
return storage.write(CACHE_KEY, cacheId, value);
}
}, {
key: 'removeCache',
value: function removeCache() {
// 这个函数不需要检查有没有开启缓存,强制清除
var cacheId = this.props.cacheId;
return storage.write(CACHE_KEY, cacheId, undefined);
}
// 关闭提示,但是不清楚缓存
// 恢复缓存的数据并删除缓存
}, {
key: 'getDecoratedComponentInstance',
// Dummy method to make Design and DesignWithDnd compatible at source code level
value: function getDecoratedComponentInstance() {
return this;
}
// Actions on design
}]);
return Design;
}(_react.PureComponent || _react.Component);
Design.propTypes = {
components: _propTypes2['default'].arrayOf(_propTypes2['default'].shape({
// 组件类型
type: _propTypes2['default'].oneOfType([_propTypes2['default'].string, _propTypes2['default'].arrayOf(_propTypes2['default'].string)]).isRequired,
// 预览这个组件的 Component
preview: _propTypes2['default'].func.isRequired,
// 预览组件的包裹层
previewItem: _propTypes2['default'].func,
// 所有预览界面上的事件都是在这个里面处理的
previewController: _propTypes2['default'].func,
// 编辑这个组件的 Component
editor: _propTypes2['default'].func.isRequired,
// 编辑组件的包裹层
editorItem: _propTypes2['default'].func,
// 传给 editor 的额外 props
editorProps: _propTypes2['default'].oneOfType([_propTypes2['default'].func, _propTypes2['default'].object]),
// 传给 preview 的额外 props
previewProps: _propTypes2['default'].oneOfType([_propTypes2['default'].func, _propTypes2['default'].object]),
// 组件是否可以拖拽
dragable: _propTypes2['default'].bool,
// 组件是否出现在添加组件的列表里面
appendable: _propTypes2['default'].bool,
// 是否显示右下角的编辑区域(编辑/加内容/删除)
// 不支持在这里配置编辑区域的按钮,参数太多。
// 如果要自定义编辑区域,可以通过重写 previewController 的方式来做。
configurable: _propTypes2['default'].bool,
// 组件是否可以编辑
// 可以选中的组件一定是可以编辑的
// 不可编辑的组件不可选中,只能展示。
// 右下角的编辑区域由 configurable 单独控制
editable: _propTypes2['default'].bool,
// 选中时是否高亮
highlightWhenSelect: _propTypes2['default'].bool,
// 组件可以添加的最大次数
limit: _propTypes2['default'].oneOfType([_propTypes2['default'].number, _propTypes2['default'].func]),
// 是否可以添加组件的回调函数,返回一个 Promise
shouldCreate: _propTypes2['default'].func
})).isRequired,
value: _propTypes2['default'].arrayOf(_propTypes2['default'].object),
// 默认选中的组件下标
defaultSelectedIndex: _propTypes2['default'].number,
// onChange(value: object)
onChange: _propTypes2['default'].func.isRequired,
// 用来渲染整个 Design 组件
preview: _propTypes2['default'].func,
// 有未保存数据关闭窗口时需要用户确认
// 离开时的确认文案新版本的浏览器是不能自定义的。
// https://www.chromestatus.com/feature/5349061406228480
confirmUnsavedLeave: _propTypes2['default'].bool,
// 是否将未保存的数据暂存到 localStorage 中
// 下次打开时如果有未保存的数据会提示从 localStorage 中恢复
// 这个 props 不支持动态修改,只会在 mount 的时候检查一次状态
cache: _propTypes2['default'].bool,
// Design 实例的缓存 id,根据这个 id 识别缓存
cacheId: _propTypes2['default'].string,
// 恢复缓存时的提示文案
cacheRestoreMessage: _propTypes2['default'].string,
// 是否禁用编辑功能
// 开启后,会忽略 components 里面的 editable 设置,全部不可编辑
disabled: _propTypes2['default'].bool,
// 一些用户自定义的全局配置
globalConfig: _propTypes2['default'].object,
// 滚动到顶部时的偏移量
scrollTopOffset: _propTypes2['default'].oneOfType([_propTypes2['default'].number, _propTypes2['default'].func]),
// 滚动到左侧时的偏移量
scrollLeftOffset: _propTypes2['default'].oneOfType([_propTypes2['default'].number, _propTypes2['default'].func]),
children: _propTypes2['default'].node,
className: _propTypes2['default'].string,
prefix: _propTypes2['default'].string
};
Design.defaultProps = {
preview: _DesignPreview2['default'],
value: [],
defaultSelectedIndex: -1,
globalConfig: {},
confirmUnsavedLeave: true,
cacheToLocalStorage: false,
cacheRestoreMessage: '提示:在浏览器中发现未提交的内容,是否使用该内容替换当前内容?',
scrollTopOffset: -10,
scrollLeftOffset: -10,
prefix: 'zent'
};
var _initialiseProps = function _initialiseProps() {
var _this2 = this;
this.onComponentValueChange = function (identity) {
return function (diff) {
var replace = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var value = _this2.props.value;
var newComponentValue = replace ? (0, _assign3['default'])((0, _defineProperty3['default'])({}, UUID_KEY, _this2.getUUIDFromValue(identity)), diff) : (0, _assign3['default'])({}, identity, diff);
var newValue = value.map(function (v) {
return v === identity ? newComponentValue : v;
});
var changedProps = Object.keys(diff);
_this2.trackValueChange(newValue);
_this2.validateComponentValue(newComponentValue, identity, changedProps).then(function (errors) {
var id = _this2.getUUIDFromValue(newComponentValue);
_this2.setValidation((0, _defineProperty3['default'])({}, id, errors));
});
};
};
this.validateComponentValue = function (value, prevValue, changedProps) {
var type = value.type;
var components = _this2.props.components;
var comp = (0, _find2['default'])(components, function (c) {
return (0, _designType.isExpectedDesginType)(c, type);
});
var validate = comp.editor.validate;
var p = validate(value, prevValue, changedProps);
return p;
};
this.onShowAddComponentOverlay = function (component) {
_this2.toggleEditOrAdd(component, true);
// 将当前组件滚动到顶部
var id = _this2.getUUIDFromValue(component);
_this2.scrollToPreviewItem(id);
};
this.onShowEditComponentOverlay = function (component) {
_this2.toggleEditOrAdd(component, false);
// 将当前组件滚动到顶部
var id = _this2.getUUIDFromValue(component);
_this2.scrollToPreviewItem(id);
};
this.onSelect = function (component) {
var id = _this2.getUUIDFromValue(component);
if (_this2.isSelected(component)) {
return;
}
_this2.setState({
selectedUUID: id,
showAddComponentOverlay: false
});
_this2.adjustHeight();
};
this.onAdd = function (component, fromSelected) {
var value = _this2.props.value;
var editor = component.editor,
defaultType = component.defaultType;
var instance = editor.getInitialValue();
instance.type = (0, _designType.getDesignType)(editor, defaultType);
var id = (0, _uuid2['default'])();
_this2.setUUIDForValue(instance, id);
/**
* 添加有两种来源:底部区域或者弹层。
* 如果来自底部的话,就在当前数组最后加;如果来自弹层就在当前选中的那个组件后面加
*/
var newValue = void 0;
if (fromSelected) {
newValue = value.slice();
var selectedUUID = _this2.state.selectedUUID;
var selectedIndex = (0, _findIndex3['default'])(value, (0, _defineProperty3['default'])({}, UUID_KEY, selectedUUID));
newValue.splice(selectedIndex + 1, 0, instance);
} else {
newValue = value.concat(instance);
}
_this2.trackValueChange(newValue);
_this2.onSelect(instance);
(0, _defer2['default'])(function () {
_this2.scrollToPreviewItem(id);
});
};
this.onDelete = function (component) {
var _props5 = _this2.props,
value = _props5.value,
components = _props5.components;
var nextIndex = -1;
var newValue = value.filter(function (v, idx) {
var skip = v !== component;
if (!skip) {
nextIndex = idx - 1;
}
return skip;
});
// 删除后默认选中前一项可选的,如果不存在则往后找一个可选项
var nextSelectedValue = findFirstEditableSibling(newValue, components, nextIndex);
var nextUUID = _this2.getUUIDFromValue(nextSelectedValue);
_this2.trackValueChange(newValue);
_this2.setState({
selectedUUID: nextUUID,
showAddComponentOverlay: false
});
_this2.adjustHeight();
(0, _defer2['default'])(function () {
_this2.scrollToPreviewItem(nextUUID);
});
};
this.onMove = function (fromIndex, toIndex) {
var value = _this2.props.value;
var newValue = value.slice();
var tmp = value[fromIndex];
newValue[fromIndex] = newValue[toIndex];
newValue[toIndex] = tmp;
_this2.trackValueChange(newValue);
};
this.setValidation = function (validation) {
_this2.setState({
validations: (0, _assign3['default'])({}, _this2.state.validations, validation)
});
_this2.adjustHeight();
};
this.validate = function () {
var _props6 = _this2.props,
value = _props6.value,
components = _props6.components;
return new Promise(function (resolve, reject) {
return Promise.all(value.map(function (v) {
var id = _this2.getUUIDFromValue(v);
var type = v.type;
var comp = (0, _find2['default'])(components, function (c) {
return (0, _designType.isExpectedDesginType)(c, type);
});
// 假如组件设置了 editable: false,不处罚校验
if (!(0, _defaultTo2['default'])(comp.editable, true)) {
return Promise.resolve((0, _defineProperty3['default'])({}, id, {}));
}
return _this2.validateComponentValue(v, v, {}).then(function (errors) {
return (0, _defineProperty3['default'])({}, id, errors);
});
})).then(function (validationList) {
var validations = _assign3['default'].apply(undefined, [{}].concat((0, _toConsumableArray3['default'])(validationList)));
_this2.setState({
showError: true,
validations: validations
}, function () {
// 跳转到第一个有错误的组件
var firstError = (0, _find2['default'])(validationList, hasValidateError);
if (firstError) {
var id = Object.keys(firstError)[0];
_this2.scrollToPreviewItem(id);
// 选中第一个有错误的组件
_this2.setState({
selectedUUID: id,
showAddComponentOverlay: false,
onShowEditComponentOverlay: true
});
}
_this2.adjustHeight();
});
// 过滤所有错误信息,将数组合并为一个对象,key 是每个组件的 id
var validationErrors = validationList.filter(hasValidateError);
var hasError = !(0, _isEmpty2['default'])(validationErrors);
if (!hasError) {
resolve();
} else {
reject(validationErrors.reduce(function (err, v) {
var key = Object.keys(v)[0];
if (key) {
err[key] = v[key];
}
return err;
}, {}));
}
});
});
};
this.markAsSaved = function () {
_this2._dirty = false;
_this2.removeCache();
};
this.selectByIndex = function (index, value) {
value = value || _this2.props.value;
index = (0, _isUndefined2['default'])(index) ? _this2.props.defaultSelectedIndex : index;
var safeIndex = getSafeSelectedValueIndex(index, value);
var safeValue = value[safeIndex];
_this2.setState({
selectedUUID: _this2.getUUIDFromValue(safeValue),
showAddComponentOverlay: false
});
};
this.isSelected = function (value) {
var selectedUUID = _this2.state.selectedUUID;
return _this2.getUUIDFromValue(value) === selectedUUID;
};
this.hasSelected = function () {
var selectedUUID = _this2.state.selectedUUID;
return !!selectedUUID;
};
this.savePreview = function (instance) {
if (instance && instance.getDecoratedComponentInstance) {
instance = instance.getDecoratedComponentInstance();
}
_this2.preview = instance;
};
this.adjustHeight = function (id) {
// 不要重复执行
if (_this2.adjustHeightTimer) {
clearTimeout(_this2.adjustHeightTimer);
_this2.adjustHeightTimer = undefined;
}
_this2.adjustHeightTimer = setTimeout(function () {
id = id || _this2.state.selectedUUID;
if (_this2.preview && _this2.preview.getEditorBoundingBox) {
var editorBB = _this2.preview.getEditorBoundingBox(id);
if (!editorBB) {
return _this2.setState({
bottomGap: 0
});
}
var previewNode = (0, _reactDom.findDOMNode)(_this2.preview);
var previewBB = previewNode && previewNode.getBoundingClientRect();
if (!previewBB) {
return;
}
var gap = Math.max(0, editorBB.bottom - previewBB.bottom);
_this2.setState({
bottomGap: gap
});
}
}, 0);
};
this.onBeforeWindowUnload = function (evt) {
if (!_this2._dirty) {
return;
}
// 这个字符串其实不会展示给用户
var confirmLeaveMessage = '页面上有未保存的数据,确定要离开吗?';
evt.returnValue = confirmLeaveMessage;
return confirmLeaveMessage;
};
this.onRestoreCacheAlertClose = function () {
_this2.setState({
showRestoreFromCache: false
});
};
this.restoreCache = function (evt) {
evt.preventDefault();
var cachedValue = _this2.readCache();
if (cachedValue !== storage.NOT_FOUND) {
_this2.trackValueChange(cachedValue, false);
_this2.setState({
showRestoreFromCache: false
});
_this2.removeCache();
}
};
this.design = function () {
return {
injections: {
getPreviewProps: function getPreviewProps(implementation) {
_this2.getPreviewProps = implementation;
}
},
getUUID: _this2.getUUIDFromValue,
validateComponentValue: _this2.validateComponentValue,
setValidation: _this2.setValidation,
markAsSaved: _this2.markAsSaved,
adjustPreviewHeight: _this2.adjustHeight
};
}();
};
exports['default'] = Design;
function tagValuesWithUUID(values) {
values.forEach(function (v) {
if (!v[UUID_KEY]) {
v[UUID_KEY] = (0, _uuid2['default'])();
}
});
}
/**
* 从 startIndex 开始往前找到第一个可以选中的值
* @param {array} value 当前的值
* @param {array} components 当前可用的组件列表
* @param {number} startIndex 开始搜索的下标
*/
function findFirstEditableSibling(value, components, startIndex) {
var loop = function loop(i) {
var val = value[i];
var type = val.type;
var comp = (0, _find2['default'])(components, function (c) {
return (0, _designType.isExpectedDesginType)(c, type);
});
if (comp && (0, _defaultTo2['default'])(comp.editable, true)) {
return val;
}
};
var valueLength = value.length;
// 往前找
for (var i = startIndex; i >= 0 && i < valueLength; i--) {
var val = loop(i);
if (val) {
return val;
}
}
// 往后找
for (var _i = startIndex + 1; _i < valueLength; _i++) {
var _val = loop(_i);
if (_val) {
return _val;
}
}
return null;
}
/**
* 根据当前的值生成一个组件使用计数
* @param {Array} value Design 当前的值
* @param {Array} components Design 支持的组件列表
*/
function makeInstanceCountMapFromValue(value, components) {
var instanceCountMap = new _InstanceCountMap2['default'](0);
(value || []).forEach(function (val) {
var comp = (0, _find2['default'])(components, function (c) {
return (0, _designType.isExpectedDesginType)(c, val.type);
});
instanceCountMap.inc((0, _designType.serializeDesignType)(comp.type));
});
return instanceCountMap;
}
function getSafeSelectedValueIndex(index, value) {
return Math.min(index, value.length - 1);
}