Home Reference Source

application/components/common/rich-editor2/plugins/anchor.js

import React from "react";
import Dialog from "material-ui/Dialog";
import TextField from "material-ui/TextField";
import CheckBox from "material-ui/Checkbox";
import FlatButton from "material-ui/FlatButton";
import RaisedButton from "material-ui/RaisedButton";
import ToolbarButton from "../common/toolbar-button";

export const SERIALIZER_RULES = [
    {
        deserialize(el, next) {
            if (el.tagName.toLowerCase() == "a") {
                const href = el.getAttribute("href");
                const target = el.getAttribute("target")
                    ? el.getAttribute("target")
                    : "";

                return {
                    object: "inline",
                    type: "anchor",
                    nodes: next(el.childNodes),
                    data: {
                        href: href,
                        target: target,
                    },
                };
            }
        },
        serialize(obj, children) {
            if (obj.object == "inline" && obj.type == "anchor") {
                return (
                    <a
                        href={obj.data.get("href")}
                        target={obj.data.get("target")}
                    >
                        {children}
                    </a>
                );
            }
        },
    },
];

class AnchorButton extends ToolbarButton {
    handleOnClick(e) {
        return anchorClickHandler.call(this.props.editor, e);
    }

    getLabel() {
        return <i className="fa fa-link"></i>;
    }
}

class AnchorNode extends React.Component {
    constructor(props) {
        super(props);

        this.parentEditor = this.props.editor;
    }

    render() {
        const { data } = this.props.node;

        const handleClick = (e) => {
            e.preventDefault();

            // open the modal and store the anchor data
            this.props.editor.setState({
                myAnchorModalOpen: true,
                myAnchorData: data,
            });
        };
        return (
            <a
                {...this.props.attributes}
                href={data.get("href")}
                target={data.get("target")}
                onClick={handleClick}
            >
                {this.props.children}
            </a>
        );
    }
}

function anchorClickHandler(e, defaultUrl = "") {
    const { editor } = this;
    const { editorContent } = this.state;
    const hasLinks = this.hasInline("anchor");

    if (hasLinks) {
        // user highlighted a link and clicked the button, so remove the link
        editor.unwrapInline("anchor");
    } else if (editorContent.selection.isExpanded) {
        // open the modal
        editor.setState({ myAnchorModalOpen: true });
        return;
    }
}

class AnchorModal extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            url: "",
            newWindow: false,
        };
    }

    closeModal() {
        this.props.editor.setState({
            myAnchorModalOpen: false,
            myAnchorData: null,
        });
        this.setState({
            url: "",
            newWindow: false,
        });
    }

    handleCancel(e) {
        this.closeModal.call(this);
    }

    componentWillReceiveProps(newProps) {
        if (newProps.editor && newProps.editor.state.myAnchorData) {
            this.setState({
                url: newProps.editor.state.myAnchorData.get("href"),
                newWindow:
                    newProps.editor.state.myAnchorData.get("target") == "_blank"
                        ? true
                        : false,
            });
        }
    }

    handleSave(e) {
        let useUrl = this.state.url;
        if (useUrl.substring(useUrl.length - 1, useUrl.length) === "\\") {
            useUrl = useUrl.substring(0, useUrl.length - 1);
        }

        // remove any existing links, expand the selection then replace the URL
        // if it's set
        this.props.editor
            .focus()
            .moveAnchorToStartOfInline()
            .moveFocusToEndOfInline()
            .unwrapInline("anchor")
            .wrapInline({
                type: "anchor",
                data: {
                    href: useUrl,
                    target: this.state.newWindow ? "_blank" : "",
                },
            });
        this.props.editor.moveToEnd();

        setTimeout(() => {
            this.closeModal.call(this);
        }, 250);
    }

    updateUrl(e) {
        this.setState({
            url: e.target.value,
        });
    }

    updateTarget(e, checked) {
        this.setState({
            newWindow: checked,
        });
    }

    handleKeyPress(e) {
        if (e.key == "Enter") {
            this.handleSave.call(this, e);
        }
    }

    render() {
        const buttons = [
            <FlatButton
                label="Cancel"
                onClick={this.handleCancel.bind(this)}
            />,
            <RaisedButton
                label="Save"
                onClick={this.handleSave.bind(this)}
                primary={true}
            />,
        ];

        return (
            <Dialog
                modal={true}
                actions={buttons}
                title="Edit Link"
                open={this.props.editor.state.myAnchorModalOpen ? true : false}
            >
                <TextField
                    floatingLabelText="URL"
                    value={this.state.url}
                    onChange={this.updateUrl.bind(this)}
                    onKeyPress={this.handleKeyPress.bind(this)}
                    fullWidth={true}
                />
                <CheckBox
                    label="Open in new window"
                    labelPosition="right"
                    checked={this.state.newWindow}
                    onCheck={this.updateTarget.bind(this)}
                />
            </Dialog>
        );
    }
}

function AnchorPlugin(options) {
    return {
        renderEditor(props, editor, next) {
            const children = next();
            return (
                <React.Fragment>
                    {children}
                    <AnchorModal editor={editor} />
                </React.Fragment>
            );
        },
        renderNode(props, editor, next) {
            const { attributes, children } = props;

            switch (props.node.type) {
                case "anchor":
                    return <AnchorNode {...props} editor={editor} />;
                default:
                    return next();
            }
        },
    };
}
export { AnchorPlugin, AnchorButton, anchorClickHandler };