Home Reference Source

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();
            }

        },
    };
}