zent
Version:
一套前端设计语言和基于React的实现
215 lines (159 loc) • 7.11 kB
Markdown
---
title: Design
path: component/design
group: Data Entry
---
## Design
H5 page editor, build your H5 pages in a WYSIWYG way.
⚠️ Warning:The `Design` component exported by Zent uses `react-dnd-html5-backend`'s `HTML5Backend`. Each React component tree can have only one instance of `HTML5Backend`. Please use `zent/lib/design/Design` to replace the default export from Zent if you are using `HTML5Backend` somewhere else. There two components are almost the same, except the one in `zent/lib/design/Design` does not depend on `HTML5Backend`.
### API
| Property | Description | Type | Default | Required |
|------|------|------|--------|--------|
| components | All available components in Design | array | [] | Yes |
| value | Current value | array | [] | Yes |
| onChange | Callback when value changes | func(value: array): void | Yes |
| defaultSelectedIndex| Default selected index in value array | number | -1 | No |
| preview | Custom Preview component | Component | DesingPreview | No |
| confirmUnsavedLeave| Show a confirm dialog if there're unsaved changes | boolean | true | No |
| cache | Cache unsaved changes to `localStorage` | boolean | false | No |
| cacheId | Cache id, must be used with `cache` | string | | Yes if `cache` is `true`, No otherwise |
| cacheRestoreMessage | Message to restore cache from `localStorage` | node | 提示:在浏览器中发现未提交的内容,是否使用该内容替换当前内容? | No |
| disabled | Is Design disabled | boolean | false | No |
| globalConfig | Global config across Design | object | | No |
| children | Additional children inside Design | node | | No |
| scrollTopOffset | Top scroll offset | number \| func | | No |
| scrollLeftOffset | Left scroll offset | number \| func | | No |
| className | Custom class name | string | | No |
| prefix | Custom prefix | string | | No |
`components` is an array, all available componets should be included in this array. Each item in this array is a component description, here are the possible options.
```js
type Component = {
// Component type, must be unique
type: string | string[],
// Default component type
// If `type` is an array, `defaultType` can be a number
// If `defaultType` is a function, it will be called with `type` as the sole argument
defaultType?: number | (string[] | string) => string
// Component to render preview
preview: ReactComponent,
// Component responsible for editing
editor: ReactComponent,
// Preview component container
previewItem?: ReactComponent,
// Preview controller, responsible for dnd, select and so on
previewController?: ReactComponent,
// Editor component container
editorItem?: ReactComponent,
// Is this component dragable?
dragable?: boolean,
// Should this component appear in the component list?
appendable?: boolean,
// Is this component configurable(edit/add/delete on the bottom right corner)?
configurable?: boolean,
// Is this component editable? Only editable components are selectable
editable?: boolean,
// Highlight preview when selected
highlightWhenSelect?: boolean,
// Maximum number of instances this component can have
// Zero is no limit
// If passing a function, return false to stop adding more
limit?: number | (count: number) => boolean,
// Callback when adding a new instance for component
// Add only if Promise resolves.
shouldCreate?: (comp: Component) => Promise,
// Additional props passed to editor
editorProps: (value: object) => object | object,
// Addtional props passed to preview
previewProps: (value: object) => object | object
}
```
Each item in `value` array must have a `type` property, `Design` uses this property to determine why component in `component` array should be used to render this value.
### Design.group
Declaration:`group(name: string): object`
`Design` supports component grouping in add component area, all you have to do is insert `Desgin.group(groupName)` to the right place in your `components` array.
```js
[
config,
Design.group('分组1'),
componentA,
componentB,
Design.group('分组2'),
componentC,
componentD
]
```
### Design Instance Methods
* `design.validate(): Promise`, trigger a validation, resolves only if there's no erro.
* `design.markAsSaved()`, tell `Desgin` data has been saved.
### stripUUID
There's a `stripUUID` method on `Design` instance, you can use this method to strip all internal ids used by `Design` before sending data to server. This may help reduce data size.
Note: calling `stripUUID` before sending data to server is optional.
### How to Implement new Design Component?
Each Desgin component are divided in two parts: Preview and Editor.
Preview is just a component which accepts `{ value: any, globalConfig: any, design: object }` as props and renders a UI with these props.
It is a little bit complex about Editor component. You are recommended to extend the `/design/lib/base/editor/DesignEditor` base class, this class has some useful methods you can use(e.g. `onChange` event handlers).
Editor has these props:
`{ value: any, onChange: func, showError: boolean, validation: object, design object }`
- `validate(value): Promise` You should resolve an error object if there're errors
- `props.design` There're some useful methods on this prop
A editor component must have these static properties:
`designType, designDescription, getInitialValue, validate`
#### Example
```jsx
// Preview
import React, { PureComponent, Component } from 'react';
export default class NoticePreview extends (PureComponent || Component) {
render() {
const { value } = this.props;
return (
<div className="rc-design-component-notice-preview">{value}</div>
);
}
}
// Editor
import React from 'react';
import { Input } from 'zent';
import { DesignEditor, ControlGroup } from '@youzan/design/base/editor/DesignEditor';
export const PLACEHOLDER = '请填写内容,如果过长,将会在手机上滚动显示';
export default class NoticeEditor extends DesignEditor {
render() {
const { value, showError, validation } = this.props;
return (
<div className="rc-design-component-notice-editor">
<ControlGroup
label="公告:"
required
showError={showError || this.getMetaProperty('content', 'touched')}
error={validation.content}
>
<Input
name="content"
placeholder={PLACEHOLDER}
value={value.content}
onChange={this.onInputChange}
onBlur={this.onInputBlur}
/>
</ControlGroup>
</div>
);
}
static designType = 'notice';
static designDescription = '公告';
static getInitialValue() {
return {
content: '',
scrollable: false
};
}
static validate(value) {
return new Promise(resolve => {
const errors = {};
const { content } = value;
if (!content || !content.trim()) {
errors.content = '请填写公告内容';
}
resolve(errors);
});
}
}
```