UNPKG

zent

Version:

一套前端设计语言和基于React的实现

1,066 lines (813 loc) 32.2 kB
'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); }