[IMP] mail: clean the emoji widget (mixin, text_emojis, char_emojis)

PURPOSE

Allow to use the text field with integrated emojis support in other apps
notably mass mailing

SPECIFICATIONS

In this commit we somehow clean code related to emojis widgets

  * simplify some code bits;
  * update comments;
  * add an option on the emojis widget to trigger ``onchange`` on a ``keydown``
    event;

LINKS

Task #2117238
PR #40045
Enterprise PR odoo/enterprise#6630
This commit is contained in:
Debauche Stéphane
2019-12-13 10:31:59 +00:00
committed by Thibault Delavallée
parent 47115a3a2a
commit 8bee94f260
4 changed files with 35 additions and 51 deletions
+10 -24
View File
@@ -1,7 +1,6 @@
odoo.define('mail.emoji_mixin', function (require) {
"use strict";
var dom = require('web.dom');
var emojis = require('mail.emojis');
/**
@@ -9,8 +8,7 @@ var emojis = require('mail.emojis');
*
* It's used to:
*
* - handle the click on an emoji from a dropdown panel and add it to the related textarea
* - replace the emoji code with the actual character, ex: ' :) ' -> ' 😊 '
* - handle the click on an emoji from a dropdown panel and add it to the related textarea/input
* - format text and wrap the emojis around <span class="o_mail_emoji"> to make them look nicer
*
* Methods are based on the collections of emojis available in mail.emojis
@@ -23,33 +21,21 @@ return {
/**
* This method should be bound to a click event on an emoji.
* (used in textarea's emojis dropdown list)
* (used in text element's emojis dropdown list)
*
* It assumes that a '_getTargetTextArea' method is defined that will return the related
* $textarea element.
*
* Some code from 'basic_composer.js' was copy/pasted to handle the cursor position in the
* textarea and handle text replacement if text was selected by the user.
* It assumes that a ``_getTargetTextElement`` method is defined that will return the related
* textarea/input element in which the emoji will be inserted.
*
* @param {MouseEvent} ev
*/
_onEmojiClick: function (ev) {
ev.preventDefault();
var unicode = ev.currentTarget.textContent.trim();
var textInput = this._getTargetTextElement($(ev.currentTarget))[0];
var selectionStart = textInput.selectionStart;
var $target = $(ev.currentTarget);
var $textarea = this._getTargetTextArea($target);
// copy/pasted from 'basic_composer.js'
var inputElement = $textarea.get(0);
var cursorPosition = inputElement ? dom.getSelectionRange(inputElement) : {start: 0, end: 0};
var inputVal = $textarea.val();
var leftSubstring = inputVal.substring(0, cursorPosition.start);
var rightSubstring = inputVal.substring(cursorPosition.end);
var newInputVal = [leftSubstring , $target.text().trim(), rightSubstring].join("");
var newCursorPosition = newInputVal.length - rightSubstring.length;
$textarea.val(newInputVal);
$textarea.focus();
$textarea[0].setSelectionRange(newCursorPosition, newCursorPosition);
textInput.value = textInput.value.slice(0, selectionStart) + unicode + textInput.value.slice(selectionStart);
textInput.focus();
textInput.setSelectionRange(selectionStart + unicode.length, selectionStart + unicode.length);
},
//--------------------------------------------------------------------------
+15 -22
View File
@@ -10,7 +10,7 @@ var _onEmojiClickMixin = MailEmojisMixin._onEmojiClick;
var QWeb = core.qweb;
/*
* Common code for FieldText and FieldChar
* Common code for FieldTextEmojis and FieldCharEmojis
*/
var FieldEmojiCommon = {
/**
@@ -19,7 +19,7 @@ var FieldEmojiCommon = {
*/
init: function () {
this._super.apply(this, arguments);
this._updatePreview =_.throttle(this._updatePreview, 1000, {leading: false});
this._triggerOnchange =_.throttle(this._triggerOnchange, 1000, {leading: false});
this.emojis = emojis;
},
@@ -28,24 +28,15 @@ var FieldEmojiCommon = {
*
* We use 'on_attach_callback' because we need the element to be attached to the form first.
* That's because the $emojisIcon element needs to be rendered outside of this $el
* (which is an textarea, that can't 'contain' any other elements).
* (which is an text element, that can't 'contain' any other elements).
*
* @override
*/
on_attach_callback: function () {
var self = this;
if (!this.$emojisIcon) {
this.$emojisIcon = $(QWeb.render(
'mail.EmojisDropdown', {
widget: this,
emojisDropdownId: 'emojis_dropdown'
}
));
this.$emojisIcon.find('.o_mail_emoji').on('click', function (ev) {
self._onEmojiClick(ev);
self._isDirty = true;
});
this.$emojisIcon = $(QWeb.render('mail.EmojisDropdown', {widget: this}));
this.$emojisIcon.find('.o_mail_emoji').on('click', this._onEmojiClick.bind(this));
this.$el.after(this.$emojisIcon);
}
@@ -74,21 +65,22 @@ var FieldEmojiCommon = {
/**
* Overridden because we need to add the Emoji to the input AND trigger
* the 'change' event to refresh the various post previews.
* the 'change' event to refresh the value.
*
* @override
* @private
*/
_onEmojiClick: function () {
_onEmojiClickMixin.apply(this, arguments);
self._isDirty = true;
this.$input.trigger('change');
},
/**
*
* By default, the 'change' event is only triggered when the textarea is blurred.
* By default, the 'change' event is only triggered when the text element is blurred.
*
* We override this method because we want to update the various post previews while
* We override this method because we want to update the value while
* the user is typing his message (and not only on blur).
*
* @override
@@ -96,7 +88,9 @@ var FieldEmojiCommon = {
*/
_onKeydown: function () {
this._super.apply(this, arguments);
this._updatePreview();
if (this.nodeOptions.onchange_on_keydown) {
this._triggerOnchange();
}
},
//--------------------------------------------------------------------------
@@ -106,21 +100,20 @@ var FieldEmojiCommon = {
/**
* Used by MailEmojisMixin, check its document for more info.
*
* @param {$.Element} $emoji
* @private
*/
_getTargetTextArea($emoji) {
_getTargetTextElement() {
return this.$el;
},
/**
* Triggers the 'change' event to refresh the various post previews.
* Triggers the 'change' event to refresh the value.
* This method is throttled to run at most once every second.
* (to avoid spamming the server while the user is typing his message)
*
* @private
*/
_updatePreview: function () {
_triggerOnchange: function () {
this.$input.trigger('change');
}
};
+6 -1
View File
@@ -14,11 +14,16 @@
}
.o_mail_emojis_dropdown {
height: 35px;
height: 25px;
width: 40px;
float: right;
margin-top: -35px;
* {
outline: none!important;
box-shadow: none!important;
}
.dropdown-toggle:after {
display: none;
}
+4 -4
View File
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<div t-name="mail.EmojisDropdown" t-attf-class="o_mail_emojis_dropdown o_mail_add_emoji dropdown">
<div t-name="mail.EmojisDropdown" class="o_mail_emojis_dropdown o_mail_add_emoji dropdown">
<button class="btn btn-block dropdown-toggle px-3 py-1"
type="button" t-att-id="emojisDropdownId"
type="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
title="Add an emoji">
<i class="fa fa-smile-o"></i>
<i class="fa fa-smile-o"/>
</button>
<div class="dropdown-menu dropdown-menu-right border-0 p-2" t-att-aria-labelledby="emojisDropdownId" style="width: 406px">
<div class="dropdown-menu dropdown-menu-right border-0 p-2" style="width: 406px">
<t t-call="mail.Composer.emojis">
<t t-set="emojis" t-value="widget.emojis"></t>
</t>