application/components/common/rich-editor2/plugins/media.js
import React from 'react';
import ToolbarButton from '../common/toolbar-button';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import TextField from 'material-ui/TextField';
import SelectField from 'material-ui/SelectField';
import Slider from 'material-ui/Slider';
import MenuItem from 'material-ui/MenuItem';
import RaisedButton from 'material-ui/RaisedButton';
import {Link} from 'react-router';
import {Row, Col} from '../../../flexbox';
import RichEditorMediaSearch from '../../../common/rich-editor/media-search';
import uuid from 'uuid';
class MediaModal extends React.Component
{
constructor(props) {
super(props);
}
handleSelectUrl(media) {
this.props.editor.setState({
'myMediaModalOpen': false
});
setTimeout(() => {
this.props.editor.splitBlock()
.insertBlock({
object: 'block',
type: 'media',
data: {
src: media.url,
uuid: ''
}
});
}, 100);
}
handleSelectMedia(media) {
this.props.editor.setState({
'myMediaModalOpen': false
}, () => {
const uuid = media.get('uuid');
const src = media.get('attachment').get('public_url');
this.props.editor
.insertBlock({
object: 'block',
type: 'media',
data: {
src: src,
uuid: uuid
}
});
});
}
handleCancel() {
this.props.editor.setState({
'myMediaModalOpen': false,
'myMediaData': null,
});
}
render() {
const actions = [
<FlatButton
onClick={this.handleCancel.bind(this)}
label='Close'
/>
];
return (
<Dialog
actions={actions}
modal={false}
open={this.props.editor.state.myMediaModalOpen ? true : false}
autoScrollBodyContent={true}
repositionOnUpdate={true}
>
<RichEditorMediaSearch onSelectUrl={this.handleSelectUrl.bind(this)} onSelectMedia={this.handleSelectMedia.bind(this)} />
</Dialog>
);
}
}
class MediaEditModal extends React.Component
{
constructor(props) {
super(props);
this.state = {
alignment: 'center',
width: 100,
link: '',
override: '',
currentKey: ''
}
}
componentWillReceiveProps(newProps) {
if (newProps.editor && newProps.editor.state.myMediaSelectedKey) {
if (this.state.currentKey == newProps.editor.state.myMediaSelectedKey) {
return;
}
const {document} = newProps.editor.value;
const node = document.getChild(newProps.editor.state.myMediaSelectedKey);
if (node) {
this.setState({
alignment: node.data.get('alignment') ? node.data.get('alignment') : 'center',
width: node.data.get('width') ? node.data.get('width') : 100,
link: node.data.get('link') ? node.data.get('link') : '',
override: node.data.get('override') ? node.data.get('override') : '',
currentKey: node.key,
uuid: node.data.get('uuid') ? node.data.get('uuid') : ''
});
}
}
}
handleCancel() {
this.setState({
currentKey: ''
});
this.props.editor.setState({
'myMediaModalEditOpen': false,
'myMediaSelectedKey': null
});
}
handleSave() {
const {document} = this.props.editor.value;
// first get the node
const node = document.getChild(this.props.editor.state.myMediaSelectedKey);
const uuid = node.data.get('uuid');
const src = node.data.get('src');
// then move the selection to the node, if we don't do this and the user has placed
// the cursor elsewhere in the text _before_ clicking the image, the image could
// be moved to that location in the text
this.props.editor.moveToRangeOfNode(node);
// remove the "old" image node
this.props.editor.removeNodeByKey(node.key);
// insert the "new" one
this.props.editor.insertBlock({
object: 'block',
type: 'media',
data: {
alignment: this.state.alignment,
width: this.state.width,
link: this.state.link,
override: this.state.override,
uuid: uuid,
src: src
}
});
this.setState({
currentKey: ''
});
setTimeout(() => {
this.props.editor.setState({
'myMediaModalEditOpen': false,
'myMediaSelectedKey': null
});
}, 100);
}
handleSelect(e, i, val) {
this.setState({
alignment: val
});
}
handleText(what, e) {
switch(what) {
case 'width':
this.setState({width: e.target.value});
break;
case 'link':
this.setState({link: e.target.value});
break;
case 'override':
this.setState({override: e.target.value});
break;
}
}
handleSlider(e, val) {
this.setState({width: val});
}
render() {
const actions = [
<FlatButton
onClick={this.handleSave.bind(this)}
primary={true}
label='Save'
/>,
<FlatButton
onClick={this.handleCancel.bind(this)}
label='Cancel'
/>
];
return (
<Dialog
actions={actions}
modal={false}
open={this.props.editor.state.myMediaModalEditOpen ? true : false}
autoScrollBodyContent={true}
repositionOnUpdate={true}
title='Edit Media Properties'
>
<TextField
floatingLabelText="Override link"
hintText="Don't forget the http"
value={this.state.link}
onChange={this.handleText.bind(this, 'link')}
fullWidth={true}
/>
<TextField
floatingLabelText="Override Caption"
hintText="Override existing caption"
value={this.state.override}
onChange={this.handleText.bind(this, 'override')}
fullWidth={true}
multiline={true}
/>
<SelectField
floatingLabelText="Alignment"
value={this.state.alignment}
onChange={this.handleSelect.bind(this)}
fullWidth={true}
>
<MenuItem value="center" primaryText="Center" />
<MenuItem value="left" primaryText="Left" />
<MenuItem value="right" primaryText="Right" />
</SelectField>
<Row middle={['xs']}>
<Col xs={10}>
<span className="fixed-label">Fixed width</span>
<Slider
value={this.state.width}
min={0}
max={100}
step={5}
onChange={this.handleSlider.bind(this)}
/>
</Col>
<Col xs={2}>
<p><strong>{this.state.width}%</strong></p>
</Col>
</Row>
{
this.state.uuid
? (
<RaisedButton
label="Open media in CEO"
primary={true}
containerElement={<Link to={'/ceo/locate/' + this.state.uuid} />}
/>
)
: ''
}
</Dialog>
);
}
}
function defaultOnClick(e) {
const {editor} = this;
const {editorContent} = this.state;
// open the modal
editor.setState({'myMediaModalOpen': true});
return;
}
export const SERIALIZER_RULES = {
deserialize(el, next) {
if (el.tagName.toLowerCase() == 'img') {
const src = el.getAttribute('src');
const uuid = el.getAttribute('data-uuid');
const override = el.getAttribute('data-override');
return {
object: 'block',
type: 'media',
data: {
src: src,
uuid: uuid,
override: override ? atob(override) : ''
}
};
}
},
serialize(obj, children) {
if (obj.object == 'block') {
switch (obj.type) {
case 'media':
const width = (obj.data.get('width') ? obj.data.get('width') : 100) + '%';
const id = obj.data.get('uuid') ? obj.data.get('uuid') : uuid.v4();
const override = obj.data.get('override') ? btoa(obj.data.get('override')) : '';
return (
<img
class="media-embed"
data-embedded-media={id}
data-uuid={id}
data-link-to={obj.data.get('link')}
src={obj.data.get('src')}
data-width={width}
data-align={obj.data.get('alignment') ? obj.data.get('alignment') : ''}
data-override={override}
/>
);
}
}
}
};
export class MediaButton extends ToolbarButton
{
handleOnClick(e) {
defaultOnClick.call(this.props.editor, e);
}
getLabel() {
return <i className='fa fa-picture-o'></i>;
}
}
export class MediaNode extends React.Component {
constructor(props) {
super(props);
this.parentEditor = this.props.editor;
}
render() {
const {data} = this.props.node;
const onClick = (e) => {
this.props.editor.focus();
this.props.editor.moveToStartOfBlock();
this.props.editor.setState({'myMediaModalEditOpen': true, 'myMediaSelectedKey': this.props.node.key});
}
let styles = {
float: ['right', 'left'].indexOf(data.get('alignment')) !== -1 ? data.get('alignment') : 'none',
width: data.get('width') ? (data.get('width') + '%') : '100%',
cursor: 'pointer',
position: 'relative'
};
switch (styles.float) {
case 'right':
styles['margin'] = '10px 0 10px 10px';
break;
case 'left':
styles['margin'] = '10px 10px 10px 0';
break;
default:
styles['margin'] = '10px 0';
break;
}
const imageStyles = {
position: 'relative',
top: 0,
left: 0,
width: '100%',
zIndex: 1000
};
return (
<div {...this.props.attributes} style={styles} className='media-node'>
<img src={data.get('src')} data-uuid={data.get('uuid')} style={imageStyles} onClick={onClick} />
</div>
);
}
}
export function MediaPlugin(options) {
return {
renderEditor(props, editor, next) {
const children = next();
return (
<React.Fragment>
{children}
<MediaModal editor={editor} />
<MediaEditModal editor={editor} />
</React.Fragment>
);
},
renderNode: (props, editor, next) => {
const {attributes, children, node} = props;
switch(node.type) {
case 'media':
return (
<MediaNode {...props} editor={editor} />
);
default:
return next();
}
},
};
}