Basic editing with some widgets

This commit is contained in:
Mathias Biilmann Christensen
2016-05-30 16:55:32 -07:00
parent 978b7290c5
commit d2aa1adf7b
23 changed files with 307 additions and 32 deletions

View File

@ -0,0 +1,24 @@
import React from 'react';
import Widgets from './Widgets';
export default class ControlPane extends React.Component {
controlFor(field) {
const { entry } = this.props;
const widget = Widgets[field.get('widget')] || Widgets._unknown;
return React.createElement(widget.Control, {
key: field.get('name'),
field: field,
value: entry.get(field.get('name')),
onChange: (value) => this.props.onChange(entry.set(field.get('name'), value))
});
}
render() {
const { collection } = this.props;
if (!collection) { return null; }
return <div>
{collection.get('fields').map((field) => <div>{this.controlFor(field)}</div>)}
</div>;
}
}

View File

@ -0,0 +1,33 @@
import React from 'react';
import ControlPane from './ControlPane';
import PreviewPane from './PreviewPane';
export default class EntryEditor extends React.Component {
constructor(props) {
super(props);
this.state = {entry: props.entry};
this.handleChange = this.handleChange.bind(this);
}
handleChange(entry) {
console.log('Got new entry: %o', entry.toObject());
this.setState({entry: entry});
}
render() {
const { collection, entry } = this.props;
return <div>
<h1>Entry in {collection.get('label')}</h1>
<h2>{entry && entry.get('title')}</h2>
<div className="cms-container">
<div className="cms-control-pane">
<ControlPane collection={collection} entry={this.state.entry} onChange={this.handleChange}/>
</div>
<div className="cms-preview-pane">
<PreviewPane collection={collection} entry={this.state.entry}/>
</div>
</div>
</div>
}
}

View File

@ -0,0 +1,23 @@
import React from 'react';
import Widgets from './Widgets';
export default class PreviewPane extends React.Component {
previewFor(field) {
const { entry } = this.props;
const widget = Widgets[field.get('widget')] || Widgets._unknown;
return React.createElement(widget.Preview, {
key: field.get('name'),
field: field,
value: entry.get(field.get('name'))
});
}
render() {
const { collection } = this.props;
if (!collection) { return null; }
return <div>
{collection.get('fields').map((field) => <div>{this.previewFor(field)}</div>)}
</div>;
}
}

30
src/components/Widgets.js Normal file
View File

@ -0,0 +1,30 @@
import UnknownControl from './widgets/UnknownControl';
import UnknownPreview from './widgets/UnknownPreview';
import StringControl from './widgets/StringControl';
import StringPreview from './widgets/StringPreview';
import MarkdownControl from './widgets/MarkdownControl';
import MarkdownPreview from './widgets/MarkdownPreview';
import ImageControl from './widgets/ImageControl';
import ImagePreview from './widgets/ImagePreview';
const Widgets = {
_unknown: {
Control: UnknownControl,
Preview: UnknownPreview
},
string: {
Control: StringControl,
Preview: StringPreview
},
markdown: {
Control: MarkdownControl,
Preview: MarkdownPreview
},
image: {
Control: ImageControl,
Preview: ImagePreview
}
};
export default Widgets;

View File

@ -0,0 +1,16 @@
import React from 'react';
export default class ImageControl extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
return <input type="file" onChange={this.handleChange}/>;
}
}

View File

@ -0,0 +1,13 @@
import React from 'react';
export default class ImagePreview extends React.Component {
constructor(props) {
super(props);
}
render() {
const { value } = this.props;
return value ? <img src={value}/> : null;
}
}

View File

@ -0,0 +1,40 @@
import React from 'react';
import {Editor, EditorState, RichUtils} from 'draft-js';
import {stateToMarkdown} from 'draft-js-export-markdown';
import {stateFromMarkdown} from 'draft-js-import-markdown';
export default class MarkdownControl extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createWithContent(stateFromMarkdown(props.value || ''))
};
this.handleChange = this.handleChange.bind(this);
this.handleKeyCommand = this.handleKeyCommand.bind(this);
}
handleChange(editorState) {
const content = editorState.getCurrentContent();
this.setState({editorState});
this.props.onChange(stateToMarkdown(content));
}
handleKeyCommand(command) {
const newState = RichUtils.handleKeyCommand(this.state.editorState, command);
if (newState) {
this.handleChange(newState);
return true;
}
return false;
}
render() {
const {editorState} = this.state;
return (
<Editor
editorState={editorState}
onChange={this.handleChange}
handleKeyCommand={this.handleKeyCommand}
/>);
}
}

View File

@ -0,0 +1,17 @@
import React from 'react';
import CommonMark from 'commonmark';
import ReactRenderer from'commonmark-react-renderer';
const parser = new CommonMark.Parser();
const renderer = new ReactRenderer();
export default class MarkdownPreview extends React.Component {
render() {
const { value } = this.props;
console.log(value);
if (value == null) { return null; }
const ast = parser.parse(value);
return React.createElement.apply(React, ['div', {}].concat(renderer.render(ast)));
}
}

View File

@ -0,0 +1,16 @@
import React from 'react';
export default class StringControl extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
return <input value={this.props.value} onChange={this.handleChange}/>;
}
}

View File

@ -0,0 +1,9 @@
import React from 'react';
export default class StringPreview extends React.Component {
render() {
const { value } = this.props;
return <span>{value}</span>;
}
}

View File

@ -0,0 +1,10 @@
import React from 'react';
export default class UnknownControl extends React.Component {
render() {
const { field } = this.props;
console.log('field: %o', field.toObject());
return <div>No control for widget '{field.get('widget')}'.</div>;
}
}

View File

@ -0,0 +1,9 @@
import React from 'react';
export default class UnknownPreview extends React.Component {
render() {
const { field } = this.props;
return <div>No preview for widget '{field.widget}'.</div>;
}
}