zent
Version:
一套前端设计语言和基于React的实现
249 lines (209 loc) • 7 kB
JavaScript
import React, { PureComponent, Component } from 'react';
import cx from 'classnames';
import PropTypes from 'prop-types';
import find from 'lodash/find';
import defaultTo from 'lodash/defaultTo';
import isFunction from 'lodash/isFunction';
import DesignPreviewItem from './DesignPreviewItem';
import DesignPreviewController from './DesignPreviewController';
import DesignEditorItem from '../editor/DesignEditorItem';
import DesignEditorAddComponent from '../editor/DesignEditorAddComponent';
import { isExpectedDesginType } from '../utils/design-type';
import { isGrouped } from '../utils/component-group';
/**
* DesignPreview 和 config 组件是相互关联的
*
* 这个组件里的一些 props 是需要 config 组件提供的
*/
class DesignPreview extends (PureComponent || Component) {
// All props in this component are injected by Design
static propTypes = {
className: PropTypes.string,
prefix: PropTypes.string,
design: PropTypes.object.isRequired,
components: PropTypes.array.isRequired,
value: PropTypes.array.isRequired,
appendableComponents: PropTypes.array,
showAddComponentOverlay: PropTypes.bool.isRequired,
selectedUUID: PropTypes.string,
getUUIDFromValue: PropTypes.func.isRequired,
onSelect: PropTypes.func.isRequired,
// 处理添加按钮的回调函数
onAdd: PropTypes.func.isRequired,
// 添加新组件
onAddComponent: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
onEdit: PropTypes.func.isRequired,
onMove: PropTypes.func.isRequired,
disabled: PropTypes.bool,
// 每个组件的实例数量
componentInstanceCount: PropTypes.object,
// 以下 props 由 config 组件提供
background: PropTypes.string
};
static defaultProps = {
background: '#f9f9f9',
disabled: false,
appendableComponents: [],
prefix: 'zent'
};
previewItems = {};
editorItems = {};
render() {
const {
components,
value,
validations,
showError,
onComponentValueChange,
componentInstanceCount,
onAddComponent,
design,
appendableComponents,
showAddComponentOverlay,
selectedUUID,
getUUIDFromValue,
onSelect,
onDelete,
onEdit,
onAdd,
onMove,
className,
prefix,
globalConfig,
disabled,
background
} = this.props;
const isComponentsGrouped = isGrouped(appendableComponents);
const children = value.map((v, idx) => {
const valueType = v.type;
const comp = find(components, c => isExpectedDesginType(c, valueType));
const PreviewItem = comp.previewItem || DesignPreviewItem;
const EditorItem = comp.editorItem || DesignEditorItem;
const id = getUUIDFromValue(v);
const selected = id === selectedUUID;
const PreviewController =
comp.previewController || DesignPreviewController;
return (
<PreviewItem
prefix={prefix}
key={id}
id={id}
ref={this.savePreviewItem(id)}
>
<PreviewController
prefix={prefix}
value={v}
globalConfig={globalConfig}
design={design}
index={idx}
dragable={defaultTo(comp.dragable, true)}
editable={defaultTo(comp.editable, true)}
configurable={defaultTo(comp.configurable, true)}
highlightWhenSelect={defaultTo(comp.highlightWhenSelect, true)}
isSelected={selected}
onSelect={onSelect}
onDelete={onDelete}
onEdit={onEdit}
onAdd={onAdd}
onMove={onMove}
component={comp.preview}
previewProps={getAdditionalProps(comp.previewProps, v)}
/>
{selected &&
!showAddComponentOverlay && (
<EditorItem
prefix={prefix}
disabled={disabled}
ref={this.saveEditorItem(id)}
>
<comp.editor
{...getAdditionalProps(comp.editorProps, v)}
value={v}
onChange={onComponentValueChange(v)}
globalConfig={globalConfig}
design={design}
validation={validations[id] || {}}
showError={showError}
prefix={prefix}
/>
</EditorItem>
)}
{selected &&
showAddComponentOverlay && (
<DesignEditorItem
ref={this.saveEditorItem(id)}
prefix={prefix}
className={cx(`${prefix}-design-add-component-overlay`, {
[`${prefix}-design-add-component-overlay--grouped`]: isComponentsGrouped,
[`${prefix}-design-add-component-overlay--mixed`]: !isComponentsGrouped
})}
>
<DesignEditorAddComponent
prefix={prefix}
fromSelected
componentInstanceCount={componentInstanceCount}
components={appendableComponents}
onAddComponent={onAddComponent}
/>
</DesignEditorItem>
)}
</PreviewItem>
);
});
const cls = cx(`${prefix}-design-preview`, className);
const hasAppendableComponent = appendableComponents.length > 0;
return (
<div className={cls} style={{ background }}>
{disabled && <div className={`${prefix}-design__disabled-mask`} />}
<div
className={cx(`${prefix}-design__item-list`, {
[`${prefix}-design__item-list--full-height`]: !hasAppendableComponent
})}
>
{children}
</div>
{hasAppendableComponent && (
<div
className={cx(`${prefix}-design__add`, {
[`${prefix}-design__add--grouped`]: isComponentsGrouped,
[`${prefix}-design__add--mixed`]: !isComponentsGrouped
})}
>
<DesignEditorAddComponent
prefix={prefix}
componentInstanceCount={componentInstanceCount}
components={appendableComponents}
onAddComponent={onAddComponent}
/>
</div>
)}
</div>
);
}
savePreviewItem = id => instance => {
this.previewItems[id] = instance;
};
saveEditorItem = id => instance => {
this.editorItems[id] = instance;
};
scrollToItem = (id, offsets) => {
const item = this.previewItems[id];
if (!item) {
return;
}
item.scrollTop(offsets);
};
getEditorBoundingBox(id) {
const item = this.editorItems[id];
if (!item) {
return;
}
return item.getBoundingBox();
}
}
function getAdditionalProps(propsOrFn, value) {
const props = isFunction(propsOrFn) ? propsOrFn(value) : propsOrFn;
return props || {};
}
export default DesignPreview;