From 93f9131450118f25c72982f7326ff820cee4aadf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Wed, 16 May 2018 10:30:55 +0200 Subject: [PATCH] [REVERT] "[IMP] mail : display mentions in green color in edit mode" This reverts commit c778de44c7cd9b7ef5a9a91738164ab0ebd0ca06. --- addons/mail/static/src/js/chatter.js | 2 +- addons/mail/static/src/js/chatter_composer.js | 2 +- addons/mail/static/src/js/composer.js | 257 ++++++------------ addons/mail/static/src/less/composer.less | 85 +++--- .../static/src/less/extended_chat_window.less | 8 +- addons/mail/static/src/xml/composer.xml | 12 +- addons/mail/static/tests/chatter_tests.js | 19 +- addons/mail/static/tests/discuss_tests.js | 9 +- addons/web/static/tests/helpers/test_utils.js | 48 ---- 9 files changed, 144 insertions(+), 298 deletions(-) diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js index 7d9f324042d..e48a7c60028 100644 --- a/addons/mail/static/src/js/chatter.js +++ b/addons/mail/static/src/js/chatter.js @@ -184,7 +184,7 @@ var Chatter = Widget.extend({ input_baseline: 14, is_log: options && options.is_log, record_name: this.record_name, - default_body: old_composer && old_composer.$input && old_composer.$input.html(), + default_body: old_composer && old_composer.$input && old_composer.$input.val(), default_mention_selections: old_composer && old_composer.mention_get_listener_selections(), }); this.composer.on('input_focused', this, function () { diff --git a/addons/mail/static/src/js/chatter_composer.js b/addons/mail/static/src/js/chatter_composer.js index 0a8088d87ae..c1660ba1c68 100644 --- a/addons/mail/static/src/js/chatter_composer.js +++ b/addons/mail/static/src/js/chatter_composer.js @@ -211,7 +211,7 @@ var ChatterComposer = composer.BasicComposer.extend({ recipient_done.then(function (partner_ids) { var context = { default_parent_id: self.id, - default_body: utils.get_text2html(self.$input.html()), + default_body: utils.get_text2html(self.$input.val()), default_attachment_ids: _.pluck(self.get('attachment_ids'), 'id'), default_partner_ids: partner_ids, default_is_log: self.options.is_log, diff --git a/addons/mail/static/src/js/composer.js b/addons/mail/static/src/js/composer.js index 45958a620f6..f89c0382554 100644 --- a/addons/mail/static/src/js/composer.js +++ b/addons/mail/static/src/js/composer.js @@ -7,6 +7,7 @@ var utils = require('mail.utils'); var config = require('web.config'); var core = require('web.core'); var data = require('web.data'); +var dom = require('web.dom'); var session = require('web.session'); var Widget = require('web.Widget'); @@ -29,7 +30,7 @@ var MentionManager = Widget.extend({ events: { "mouseover .o_mention_proposition": "on_hover_mention_proposition", - "click .o_mention_proposition": "_onClickMentionItem", + "click .o_mention_proposition": "on_click_mention_item", }, init: function (parent, options) { @@ -52,6 +53,49 @@ var MentionManager = Widget.extend({ this.$('.o_mention_proposition').removeClass('active'); $elem.addClass('active'); }, + on_click_mention_item: function (event) { + event.preventDefault(); + + var text_input = this.composer.$input.val(); + var id = $(event.currentTarget).data('id'); + var selected_suggestion = _.find(_.flatten(this.get('mention_suggestions')), function (s) { + return s.id === id; + }); + var substitution = selected_suggestion.substitution; + if (!substitution) { // no substitution string given, so use the mention name instead + // replace white spaces with non-breaking spaces to facilitate mentions detection in text + selected_suggestion.name = selected_suggestion.name.replace(/ /g, NON_BREAKING_SPACE); + substitution = this.active_listener.delimiter + selected_suggestion.name; + } + var get_mention_index = function (matches, cursor_position) { + for (var i=0; i 0) { return false; } - var search_str = textVal.substring(delimiter_position, cursorPosition); + var search_str = text_val.substring(delimiter_position, cursor_position); var pattern = "(^"+delimiter+"|(^\\s"+delimiter+"))"; var regex_start = new RegExp(pattern, "g"); search_str = search_str.replace(/^\s\s*|^[\n\r]/g, ''); @@ -203,8 +247,6 @@ var MentionManager = Widget.extend({ /** * Returns the matches (as RexExp.exec does) for the mention in the input text - * - * @private * @param {String} input_text: the text to search matches * @param {Object} listener: the listener for which we want to find a match * @returns {Object[]} matches in the same format as RexExp.exec() @@ -228,32 +270,6 @@ var MentionManager = Widget.extend({ } return result; }, - /** - * Get cursor position in contenteditable div with it's inner html content. - * - * @private - * @return a current cursor position - */ - _getSelectionPositions: function () { - if (window.getSelection && window.getSelection().getRangeAt) { - var range = window.getSelection().getRangeAt(0); - var selectedObj = window.getSelection(); - var rangeCount = 0; - var childNodes = selectedObj.anchorNode.parentNode.childNodes; - for (var i = 0; i < childNodes.length; i++) { - if (childNodes[i] === selectedObj.anchorNode) { - break; - } - if (childNodes[i].outerHTML) - rangeCount += childNodes[i].outerHTML.length; - else if (childNodes[i].nodeType === 3) { - rangeCount += childNodes[i].textContent.length; - } - } - return range.startOffset + rangeCount; - } - return -1; - }, /** * @private * @param {integer} keycode @@ -312,23 +328,9 @@ var MentionManager = Widget.extend({ this.open = false; } }, - /* - * Set cursor position after mention is added. - * @private - * @paran {int} node - id of childnode to set cursor position after it. - */ - _setCursorPosition: function (nodeID) { - var range = document.createRange(); - var selection = window.getSelection(); - range.setStart(document.getElementById(nodeID).nextSibling, 1); - range.collapse(true); - selection.removeAllRanges(); - selection.addRange(range); - this.composer.$input.focus(); - }, - _validate_selection: function (selection, inputMentions) { + _validate_selection: function (selection, input_mentions) { var validated_selection = []; - _.each(inputMentions, function (mention) { + _.each(input_mentions, function (mention) { var validated_mention = _.findWhere(selection, {name: mention.slice(1)}); if (validated_mention) { validated_selection.push(validated_mention); @@ -337,60 +339,15 @@ var MentionManager = Widget.extend({ return validated_selection; }, - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - - /** - * after click on mention item it set to a contenteditable div with it's new - * cursor postion - * - * @private - * @param {KeyboardEvent} event - to get a current selected mention from - * list and eventlistener. - */ - _onClickMentionItem: function (event) { - event.preventDefault(); - - var textInput = this.composer.$input.html(); - var id = $(event.currentTarget).data('id'); - var selectedSuggestion = _.find(_.flatten(this.get('mention_suggestions')), function (s) { - return s.id === id; + // Cursor position and selection utils + _get_selection_positions: function () { + var el = this.composer.$input.get(0); + return el ? dom.getSelectionRange(el) : {start: 0, end: 0}; + }, + _set_cursor_position: function (pos) { + this.composer.$input.each(function (index, elem) { + dom.setSelectionRange(elem, {start: pos, end: pos}); }); - var substitution = selectedSuggestion.substitution; - if (!substitution) { // no substitution string given, so use the mention name instead - // replace white spaces with non-breaking spaces to facilitate mentions detection in text - selectedSuggestion.name = selectedSuggestion.name.replace(/ /g, NON_BREAKING_SPACE); - substitution = _.escape(this.active_listener.delimiter + selectedSuggestion.name); - } - var getMentionIndex = function (matches, cursorPosition) { - for (var i=0; i" + substitution + " " + textRight; - this.composer.$input.html(newTextInput); - this._setCursorPosition(nodeID); - this.set('mention_suggestions', []); }, }); @@ -398,8 +355,8 @@ var MentionManager = Widget.extend({ var BasicComposer = Widget.extend({ template: "mail.ChatComposer", events: { - "keydown .o_composer_input": "_onKeydown", - "keyup .o_composer_input": "_onKeyup", + "keydown .o_composer_input textarea": "on_keydown", + "keyup .o_composer_input": "on_keyup", "change input.o_input_file": "on_attachment_change", "click .o_composer_button_send": "send_message", "click .o_composer_button_add_attachment": "on_click_add_attachment", @@ -410,7 +367,6 @@ var BasicComposer = Widget.extend({ 'focusout .o_composer_button_emoji': '_onEmojiButtonFocusout', 'focus .o_mail_emoji_container .o_mail_emoji': '_onEmojiImageFocus', 'click .o_mail_emoji_container .o_mail_emoji': '_onEmojiImageClick', - 'input .o_composer_input': '_removeMention', }, // RPCs done to fetch the mention suggestions are throttled with the following value MENTION_THROTTLE: 200, @@ -487,12 +443,12 @@ var BasicComposer = Widget.extend({ this.$attachment_button = this.$(".o_composer_button_add_attachment"); this.$attachments_list = this.$('.o_composer_attachments_list'); - this.$input = this.$('.o_composer_input'); + this.$input = this.$('.o_composer_input textarea'); this.$input.focus(function () { self.trigger('input_focused'); }); - this.$input.html(this.options.default_body); - this.$input.css('min-height', this.options.input_min_height); + this.$input.val(this.options.default_body); + dom.autoresize(this.$input, {parent: this, min_height: this.options.input_min_height}); // Attachments $(window).on(this.fileupload_id, this.on_attachment_loaded); @@ -514,7 +470,7 @@ var BasicComposer = Widget.extend({ // behavior for the chatter composer //Removing unwanted extra spaces from message - var value = _.escape(this.$input.text()).trim(); + var value = _.escape(this.$input.val()).trim(); value = value.replace(/(\r|\n){2,}/g, '

'); value = value.replace(/(\r|\n)/g, '
'); @@ -545,7 +501,7 @@ var BasicComposer = Widget.extend({ clear_composer: function() { // Empty input, selected partners and attachments - this.$input.html(''); + this.$input.val(''); this.mention_manager.reset_selections(); this.set('attachment_ids', []); }, @@ -557,7 +513,7 @@ var BasicComposer = Widget.extend({ getState: function () { return { attachments: this.get('attachment_ids'), - text: this.$input.html(), + text: this.$input.val(), }; }, @@ -572,7 +528,7 @@ var BasicComposer = Widget.extend({ setState: function (state) { this.set('attachment_ids', state.attachments); - this.$input.html(state.text); + this.$input.val(state.text); }, /** @@ -581,18 +537,8 @@ var BasicComposer = Widget.extend({ should_send: function (event) { return !event.shiftKey; }, - /** - * _onKeydown event is triggered when is key is pressed - * - on UP and DOWN arrow is pressed then event prevents it's default - * behaviour if mention manager is open else it break it. - * - on ENTER key is pressed and mention manager is open then event - * prevents it's default behaviour else check if ControlKey is - * pressed or not if yes then it send a message - * - * @private - * @param {KeyboardEvent} event - */ - _onKeydown: function (event) { + + on_keydown: function (event) { switch(event.which) { // UP, DOWN, TAB: prevent moving cursor if navigation in mention propositions case $.ui.keyCode.UP: @@ -617,16 +563,7 @@ var BasicComposer = Widget.extend({ } }, - /** - * _onKeyup event is triggered when key is released. - * on ESCAP key it close the mention suggestion dropdown menu. - * on ENTER key it selects that mention and add to a content editable div. - * on UP or DOWN key it highlights previous or next mention respectively. - * - * @private - * @param {KeyboardEvent} event - */ - _onKeyup: function (event) { + on_keyup: function (event) { switch(event.which) { // ESCAPED KEYS: do nothing case $.ui.keyCode.END: @@ -743,13 +680,6 @@ var BasicComposer = Widget.extend({ attachments: this.get('attachment_ids'), })); }, - // remove mention when user try to edit or remove it. - _removeMention: function (event) { - var anchorNode = window.getSelection().anchorNode; - if (anchorNode.textContent.startsWith('@') && anchorNode.parentNode.tagName === 'A') { - anchorNode.parentNode.remove(); - } - }, // Mention mention_fetch_throttled: function (model, method, kwargs) { @@ -835,14 +765,8 @@ var BasicComposer = Widget.extend({ }, // Others - /** - * used to check if contenteditable div is empty or not - * - * @return - it returns length of attachment and trim content of input box - * tp check for empty. - */ is_empty: function () { - return !this.$input.text().trim() && !this.$('.o_attachments').children().length; + return !this.$input.val().trim() && !this.$('.o_attachments').children().length; }, focus: function () { this.$input.focus(); @@ -901,7 +825,7 @@ var BasicComposer = Widget.extend({ if (this.$emojisContainer.parent().length) { this._hideEmojis(); } else { - this.$emojisContainer.appendTo(this.$('.o_composer_container')); + this.$emojisContainer.appendTo(this.$('.o_composer')); } }, /** @@ -925,31 +849,6 @@ var BasicComposer = Widget.extend({ _onEmojiImageFocus: function () { clearTimeout(this._hideEmojisTimeout); }, - /** - * set cursor position at the end in input box - * - * @todo move that kind of code in a utility file. Maybe dom.js - * - * @private - * @param {Element} el - */ - _placeCaretAtEnd: function (el) { - el.focus(); - if (typeof window.getSelection !== "undefined" - && typeof document.createRange !== "undefined") { - var range = document.createRange(); - range.selectNodeContents(el); - range.collapse(false); - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - } else if (typeof document.body.createTextRange !== "undefined") { - var textRange = document.body.createTextRange(); - textRange.moveToElementText(el); - textRange.collapse(false); - textRange.select(); - } - }, /** * Called when an emoji is clicked -> adds it in the , focuses the * and closes the emoji panel. @@ -958,8 +857,8 @@ var BasicComposer = Widget.extend({ * @param {Event} ev */ _onEmojiImageClick: function (ev) { - this.$input.html(this.$input.html() + " " + $(ev.currentTarget).data('emoji') + " "); - this._placeCaretAtEnd(this.$input[0]); + this.$input.val(this.$input.val() + " " + $(ev.currentTarget).data('emoji') + " "); + this.$input.focus(); this._hideEmojis(); }, }); diff --git a/addons/mail/static/src/less/composer.less b/addons/mail/static/src/less/composer.less index 976473f6e1e..9213fb6c416 100644 --- a/addons/mail/static/src/less/composer.less +++ b/addons/mail/static/src/less/composer.less @@ -10,7 +10,7 @@ position: relative; background: white; - input { + textarea, input { border: none; } } @@ -27,19 +27,9 @@ } .o_composer { - .o_composer_input[contenteditable=true]:empty:before { - content: attr(placeholder); - opacity: 0.4; - } - .o_composer_input { - border: none; - box-shadow: none; - cursor: text; - display: inline-block; - height: 100%; - padding: 0px 0px @o-mail-chatter-gap; - white-space: pre-wrap; - word-break: break-all; + .o_composer_input > textarea { + width: 100%; + resize: none; } .o_composer_mention_dropdown .o_mention_proposition > a { .o-text-overflow(); @@ -68,16 +58,17 @@ color: @odoo-brand-optional; } } - } - .o_mail_emoji_container { - padding: 0 @o-mail-chatter-gap @o-mail-chatter-gap 0; - border: 1px solid @gray-lighter-dark; - background-color: @gray-lighter; + .o_mail_emoji_container { + padding: 0 @o-mail-chatter-gap @o-mail-chatter-gap 0; + border: 1px solid @gray-lighter-dark; + border-width: 1px 0 0 0; + background-color: @gray-lighter; - .o_mail_emoji { - margin: @o-mail-chatter-gap 0 0 @o-mail-chatter-gap; - cursor: pointer; // only in the popover + .o_mail_emoji { + margin: @o-mail-chatter-gap 0 0 @o-mail-chatter-gap; + cursor: pointer; // only in the popover + } } } @@ -103,13 +94,19 @@ .o_composer { border-radius: 2px; + textarea { + padding-right: @grid-gutter-width; // avoid overlaps betwenn textarea and o_composer_button_full_composer + } + .o_mail_emoji_container { + margin: 0 -@o-mail-chatter-gap; + } } .o_composer, .o_composer_subject { border: 1px solid @gray-lighter-dark; - input { + textarea, input { padding: 0 0 @o-mail-chatter-gap; border: none; } @@ -121,9 +118,6 @@ .o_chatter_composer_tools { border-top: 1px solid @gray-lighter-dark; - @media (max-width: @screen-xs-max) { - margin-top: 10px; - } } &:not(.o_chat_inline_composer) { @@ -142,24 +136,26 @@ &.o_chat_inline_composer { .o_composer_container { - .o-flex(1, 1, auto); + .o-flex(1, 0, auto); } .o_composer { - display: flex; - min-height: 40px; padding: @o-mail-chatter-gap @o-mail-chatter-gap 0 @o-mail-chatter-gap; border-radius: 2px 0 0 2px; border-right-width: 0; - } - .o_mail_emoji_container { - text-align: right; - border-width: 1px; + + .o_mail_emoji_container { + text-align: right; + border-right-width: 1px; + } } .o_composer_input { + .o-flex-display(); .o-flex-flow(row, wrap); } .o_composer_text_field { .o-flex(1, 1, 50%); + margin: 0; + padding: 0; } .o_chatter_composer_tools { .o-flex(0, 1, auto); @@ -191,25 +187,32 @@ border-top: 1px solid @gray-lighter-dark; width: 100%; } - .o_mail_emoji_container { - .o-position-absolute(auto, 0, 100%, 0); - border-bottom-width: 1px; + .o_composer { + .o_mail_emoji_container { + .o-position-absolute(auto, 0, 100%, 0); + border-bottom-width: 1px; + } } .o_composer_input { + .o-flex-display(); .o-flex-flow(row, nowrap); + textarea.o_composer_text_field { + .o-flex(1, 1, auto); + } + .o_chatter_composer_tools { .o-flex(0, 0, auto); } + @media (max-width: @screen-xs-max) { + .o_composer_button_send { + display: inline-block; + } + } } .o_composer_button_send, .o_chatter_avatar { display: none; } - @media (max-width: @screen-xs-max) { - .o_composer_button_send { - display: inline-block; - } - } } } diff --git a/addons/mail/static/src/less/extended_chat_window.less b/addons/mail/static/src/less/extended_chat_window.less index fcf92cf052a..3ae73dee861 100644 --- a/addons/mail/static/src/less/extended_chat_window.less +++ b/addons/mail/static/src/less/extended_chat_window.less @@ -42,14 +42,10 @@ } .o_chatter_composer_tools { .o-position-absolute(@right: 0px, @top: 0px); - - @media (max-width: @screen-xs-max) { - .o-position-absolute(@right: 6px, @top: 0px); - } button { - padding: 8px; + padding: 10px; @media (max-width: @screen-xs-max) { - padding: 7px 6px 0px 6px; + padding: 13px; } &.btn-icon { font-size: 1.3em; diff --git a/addons/mail/static/src/xml/composer.xml b/addons/mail/static/src/xml/composer.xml index 2795a0bb724..b46af297928 100644 --- a/addons/mail/static/src/xml/composer.xml +++ b/addons/mail/static/src/xml/composer.xml @@ -8,11 +8,13 @@
-
-
-