API Docs for:
Show:

File: components/CommentForm.js

/** @jsx React.DOM */
"use strict";

var React = require("react/addons");
var $ = require("jquery");
var _ = require("lodash");

var ElasticTextarea = require("./ElasticTextarea");

var Button = require("react-bootstrap/Button");
var OverlayTrigger = require("react-bootstrap/OverlayTrigger");
var Tooltip = require("react-bootstrap/Tooltip");
var Label = require("react-bootstrap/Label");

function scrollElBottom(el, padding) {
    padding = padding || 0;
    var $el = $(el);
    window.scrollTo(0, $el.offset().top - $(window).height() + $el.height() + padding);
}


// http://stackoverflow.com/a/488073/153718
function isScrolledIntoView(elem, padding) {
    padding = padding || 0;
    var docViewTop = $(window).scrollTop();
    var docViewBottom = docViewTop + $(window).height();

    var elemTop = $(elem).offset().top;
    var elemBottom = elemTop + $(elem).height() + padding;

    return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}


/**
 * Two mode comment form
 *
 * When single line mode
 *
 *   - Enter key submits the value
 *   - Shift+Enter forces a line break and enables the multiline mode
 *
 * When in multiline mode:
 *
 *   - Enter key adds an additional line break
 *   - Ctrl+Enter submits the value
 *
 * @namespace components
 * @class CommentForm
 * @constructor
 * @param {Object} props
 * @param {Function} [props.onSubmit] Called when the form is submitted with
 * the button or the keyboard shortcut
 */
var CommentForm = React.createClass({

    propTypes: {
        onSubmit: React.PropTypes.func
    },

    getDefaultProps: function() {
        return {
            onSubmit: function(){}
        };
    },

    getInitialState: function() {
        return {
            comment: ""
        };
    },

    componentDidMount: function() {
        this.scrollToCommentButton = _.throttle(this.scrollToCommentButton, 100);
    },

    isMultilineMode: function() {
        return this.state.comment.split("\n").length > 1;
    },

    /**
     * Return true if the textarea has a proper comment
     *
     * @method hasComment
     * @return {Boolean}
     */
    hasComment: function() {
        return !!this.state.comment.trim();
    },

    _handleKeyDown: function(e) {
        if (e.key !== "Enter") return;

        // Ctrl+Enter always saves the comment
        if (e.ctrlKey) {
            e.preventDefault();
            this._submit();
            return;
        }

        // Shift+Enter or plain enter in multiline mode inserts a line break
        if (e.shiftKey || this.isMultilineMode()) return;

        e.preventDefault();
        this._submit();
    },


    /**
     * Clear the textarea
     *
     * @method clear
     */
    clear: function() {
        this.setState({ comment: "" });
    },

    /**
     * Get the textarea value
     *
     * @method getValue
     * @return {String}
     */
    getValue: function() {
        return this.state.comment;
    },

    /**
     * Emit onSubmit event with the current textarea value
     *
     * @private
     * @method _submit
     */
    _submit: function() {
        if (!this.hasComment()) {
            console.log("No comment, no submit");
            return;
        }

        this.props.onSubmit({
            comment: this.state.comment,
            clear: this.clear,
            scrollToCommentButton: this.scrollToCommentButton
        });
        this.refs.textarea.getDOMNode().focus();
    },

    _handleCommentChange: function(e) {
        this.setState({ comment: e.target.value });
    },

    getTipText: function() {
        var tip = {
            title: "Yksirivitila",
            desc: "Enter-näppäin lähettää kommentin. Paina Shift+enter siirtyäksesi monirivitilaan.",
            bsStyle: "default"
        };

        if (this.isMultilineMode()) {
            tip.title = "Monirivitila";
            tip.desc = "Enter-näppäin lisää rivin vaihdon. Paina Ctrl+Enter lähettääksesi kommentin.";
            tip.bsStyle = "success";
        }

        return tip;
    },

    scrollToCommentButton: function() {
        if (!this.refs.commentButton) {
            console.error("Cannot scroll to comment button. Element not available!");
            return;
        }

        var $el = $(this.refs.commentButton.getDOMNode());
        if (isScrolledIntoView($el, 10)) return;
        scrollElBottom($el, 50);
    },

    render: function() {
        var self = this;
        var tip = this.getTipText();
        return (
            <div className="CommentForm">

                <OverlayTrigger placement="left" overlay={<Tooltip>{tip.desc}</Tooltip>}>
                    <Label bsStyle={tip.bsStyle} className="linemode-tooltip">{tip.title}</Label>
                </OverlayTrigger>

                <ElasticTextarea
                    placeholder="Kommentti..."
                    ref="textarea"
                    className="form-control"
                    value={this.state.comment}
                    onChange={this._handleCommentChange}
                    minRows={1}
                    onKeyDown={this._handleKeyDown}
                    onResize={function(e) {
                        if (e.active) self.scrollToCommentButton();
                    }}
                />

                <div className="ticket-update-buttons">
                    <Button
                        ref="commentButton"
                        onClick={this._submit}
                        disabled={!this.hasComment()} >
                        {this.props.children}
                    </Button>
                </div>

            </div>
        );
    },

});


module.exports = CommentForm;