[IMP] mail, mass_mailing, im_livechat: port emoji fields to OWL

This ports the `text_emojis` and `char_emojis` fields to OWL.

X-original-commit: 65b3e551aee0ea451a7f48c6778ce78931bff3e8
Part-of: odoo/odoo#101350
This commit is contained in:
Antoine Guenet
2022-09-28 09:38:55 +02:00
parent 9d156f289f
commit 9dbbaa7a09
13 changed files with 135 additions and 184 deletions
@@ -16,7 +16,7 @@ const commonSteps = [tour.stepUtils.showAppsMenuItem(), {
}, {
trigger: 'div[name="script_step_ids"] .o_field_x2many_list_row_add a'
}, {
trigger: 'textarea[name="message"]',
trigger: 'textarea#message',
run: 'text Step 1'
}, {
trigger: 'button:contains("Save & New")'
@@ -25,7 +25,7 @@ const commonSteps = [tour.stepUtils.showAppsMenuItem(), {
in_modal: false,
run: () => {}
}, {
trigger: 'textarea[name="message"]',
trigger: 'textarea#message',
run: 'text Step 2'
}, {
trigger: 'button:contains("Save & New")'
@@ -34,7 +34,7 @@ const commonSteps = [tour.stepUtils.showAppsMenuItem(), {
in_modal: false,
run: () => {}
}, {
trigger: 'textarea[name="message"]',
trigger: 'textarea#message',
run: 'text Step 3'
}];
@@ -68,7 +68,7 @@ tour.register('im_livechat_chatbot_steps_sequence_with_move_tour', {
in_modal: false,
run: () => {}
}, {
trigger: 'textarea[name="message"]',
trigger: 'textarea#message',
run: 'text Step 4'
}, {
trigger: 'button:contains("Save & New")'
@@ -77,7 +77,7 @@ tour.register('im_livechat_chatbot_steps_sequence_with_move_tour', {
in_modal: false,
run: () => {}
}, {
trigger: 'textarea[name="message"]',
trigger: 'textarea#message',
run: 'text Step 5'
}, {
trigger: 'button:contains("Save & Close")'
@@ -108,7 +108,7 @@ tour.register('im_livechat_chatbot_steps_sequence_with_move_tour', {
}, {
trigger: 'div[name="script_step_ids"] .o_field_x2many_list_row_add a'
}, {
trigger: 'textarea[name="message"]',
trigger: 'textarea#message',
run: 'text Step 6'
}, {
trigger: 'button:contains("Save & Close")'
@@ -0,0 +1,19 @@
/** @odoo-module **/
import emojis from '@mail/js/emojis';
const { Component, useRef, onMounted } = owl;
export class EmojisDropdown extends Component {
setup() {
this.toggleRef = useRef('toggleRef');
this.emojis = emojis;
super.setup();
onMounted(() => {
new Dropdown(this.toggleRef.el, {
popperConfig: { placement: 'bottom-end', strategy: 'fixed' },
});
});
}
};
EmojisDropdown.template = 'mail.EmojisDropdown';
+6 -6
View File
@@ -28,10 +28,10 @@ export default {
*
* @param {MouseEvent} ev
*/
_onEmojiClick: function (ev) {
var unicode = ev.currentTarget.textContent.trim();
var textInput = this._getTargetTextElement($(ev.currentTarget))[0];
var selectionStart = textInput.selectionStart;
onEmojiClick(ev) {
const unicode = ev.currentTarget.textContent.trim();
const textInput = this._getTargetTextElement($(ev.currentTarget));
const selectionStart = textInput.selectionStart;
textInput.value = textInput.value.slice(0, selectionStart) + unicode + textInput.value.slice(selectionStart);
textInput.focus();
@@ -49,7 +49,7 @@ export default {
*
* @param {String} message a text message to format
*/
_formatText: function (message) {
_formatText(message) {
message = escape(message);
message = this._wrapEmojis(message);
message = message.replace(/(?:\r\n|\r|\n)/g, '<br>');
@@ -63,7 +63,7 @@ export default {
*
* @param {String} message
*/
_wrapEmojis: function (message) {
_wrapEmojis(message) {
emojis.forEach(function (emoji) {
message = message.replace(
new RegExp(emoji.unicode.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'),
+3 -15
View File
@@ -17,20 +17,11 @@ $o-mail-emoji-height: 2rem;
}
}
.o_form_view {
// Emojis widgets should hide the emoji dropdown button when the field is invisible.
// This is necessary because the button is added *after* the main element (and not inside)
// (see '_attachEmojisDropdown' for more details)
.o_invisible_modifier + .o_mail_add_emoji, .o_invisible_modifier + textarea + .o_mail_add_emoji{
display: none !important;
}
}
.o_mail_emojis_dropdown {
height: $o-mail-emoji-height;
width: 40px;
float: right;
bottom: 33px;
bottom: 26px;
margin-bottom: -$o-mail-emoji-height;
* {
@@ -50,15 +41,12 @@ $o-mail-emoji-height: 2rem;
margin-right: 20px;
}
.o_mail_emojis_dropdown_textarea{
bottom: 40px;
.o_mail_emojis_dropdown_textarea {
bottom: 30px;
}
.o_xxs_form_view {
.o_mail_emojis_dropdown {
bottom: 50px;
}
.o_mail_add_emoji {
.dropdown-menu {
max-width: 320px;
@@ -1,16 +1,28 @@
/** @odoo-module **/
import { CharField } from "@web/views/fields/char/char_field";
import { patch } from "@web/core/utils/patch";
import MailEmojisMixin from '@mail/js/emojis_mixin';
import EmojisFieldCommon from '@mail/views/fields/emojis_field_common';
import { EmojisDropdown } from '@mail/js/emojis_dropdown';
import { EmojisFieldCommon } from '@mail/views/fields/emojis_field_common';
import { registry } from "@web/core/registry";
import basicFields from 'web.basic_fields';
import registry from 'web.field_registry';
const { useRef } = owl;
/**
* Extension of the FieldChar that will add emojis support
*/
var EmojisCharField = basicFields.FieldChar.extend(MailEmojisMixin, EmojisFieldCommon);
export class EmojisCharField extends CharField {
setup() {
super.setup();
this.targetEditElement = useRef('input');
this._setupOverride();
}
};
registry.add('char_emojis', EmojisCharField);
export default EmojisCharField;
patch(EmojisCharField.prototype, 'emojis_char_field_mail_mixin', MailEmojisMixin);
patch(EmojisCharField.prototype, 'emojis_char_field_field_mixin', EmojisFieldCommon);
EmojisCharField.template = 'mail.EmojisCharField';
EmojisCharField.components = { ...CharField.components, EmojisDropdown };
EmojisCharField.additionalClasses = [...(CharField.additionalClasses || []), 'o_field_text'];
registry.category("fields").add("char_emojis", EmojisCharField);
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="mail.EmojisCharField" t-inherit="web.CharField" t-inherit-mode="primary" owl="1">
<xpath expr="//span[1]" position="attributes">
<attribute name="t-ref">targetReadonlyElement</attribute>
</xpath>
<xpath expr="/*[last()]/*[last()]" position="after">
<EmojisDropdown onEmojiClick="onEmojiClick" readonly="props.readonly" type="'char'"/>
</xpath>
</t>
</templates>
@@ -1,144 +1,39 @@
/** @odoo-module **/
import { debounce } from "@web/core/utils/timing";
import emojis from '@mail/js/emojis';
import MailEmojisMixin from '@mail/js/emojis_mixin';
import core from 'web.core';
var _onEmojiClickMixin = MailEmojisMixin._onEmojiClick;
var QWeb = core.qweb;
const _onEmojiClickMixin = MailEmojisMixin.onEmojiClick;
const { useRef, onMounted } = owl;
/*
* Common code for FieldTextEmojis and FieldCharEmojis
* Common code for EmojisTextField and EmojisCharField
*/
var EmojisFieldCommon = {
/**
* @override
* @private
*/
init: function () {
this._super.apply(this, arguments);
if (this.nodeOptions.keydown_debounce_delay === undefined) {
this.nodeOptions.keydown_debounce_delay = 2000;
}
this._triggerOnchange = debounce(this._triggerOnchange, this.nodeOptions.keydown_debounce_delay);
this.emojis = emojis;
},
/**
* @override
*/
on_attach_callback: function () {
this._attachEmojisDropdown();
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @override
* @private
*/
_render: function () {
this._super.apply(this, arguments);
if (this.mode !== 'edit') {
this.$el.html(this._formatText(this.$el.text()));
}
},
/**
* Overridden because we need to add the Emoji to the input AND trigger
* the 'change' event to refresh the value.
*
* @override
* @private
*/
_onEmojiClick: function () {
_onEmojiClickMixin.apply(this, arguments);
this._isDirty = true;
this.$input.trigger('change');
},
/**
*
* By default, the 'change' event is only triggered when the text element is blurred.
*
* We override this method because we want to update the value while
* the user is typing their message (and not only on blur).
*
* @override
* @private
*/
_onKeydown: function () {
this._super.apply(this, arguments);
if (this.nodeOptions.onchange_on_keydown) {
this._triggerOnchange();
export const EmojisFieldCommon = {
_setupOverride() {
this.targetReadonlyElement = useRef('targetReadonlyElement');
this.emojisDropdown = useRef('emojisDropdown');
if (this.props.readonly) {
onMounted(() => {
this.targetReadonlyElement.el.innerHTML = this._formatText(this.targetReadonlyElement.el.textContent);
});
}
this.onEmojiClick = this._onEmojiClick.bind(this);
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
_onEmojiClick() {
_onEmojiClickMixin.apply(this, arguments);
this.props.update(this._getTargetTextElement().value);
},
/**
* Used by MailEmojisMixin, check its document for more info.
*
* @private
*/
_getTargetTextElement() {
return this.$el;
return this.props.readonly ? this.targetReadonlyElement.el : this.targetEditElement.el;
},
/**
* Triggers the 'change' event to refresh the value.
* This method is debounced to run after given debouce delay on typing ends.
* (to avoid spamming the server while the user is typing their message)
*
* @private
*/
_triggerOnchange: function () {
this.$input.trigger('change');
},
/**
* This will add an emoji button that shows the emojis selection dropdown.
*
* Should be used inside '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 text element, that can't 'contain' any other elements).
*
* @private
*/
_attachEmojisDropdown: function () {
if (!this.$emojisIcon) {
this.$emojisIcon = $(QWeb.render('mail.EmojisDropdown', {widget: this}));
this.$emojisIcon.find('.o_mail_emoji').on('click', this._onEmojiClick.bind(this));
if (this.$el.filter('span.o_field_translate').length) {
// multi-languages activated, place the button on the left of the translation button
this.$emojisIcon.addClass('o_mail_emojis_dropdown_translation');
}
if (this.$el.filter('textarea').length) {
this.$emojisIcon.addClass('o_mail_emojis_dropdown_textarea');
}
this.$el.last().after(this.$emojisIcon);
}
if (this.mode === 'edit') {
this.$emojisIcon.show();
} else {
this.$emojisIcon.hide();
}
},
destroy() {
this._super(...arguments);
if (this.$emojisIcon) {
this.$emojisIcon.remove();
}
}
};
export default EmojisFieldCommon;
@@ -1,16 +1,26 @@
/** @odoo-module **/
import { TextField } from "@web/views/fields/text/text_field";
import { patch } from "@web/core/utils/patch";
import MailEmojisMixin from '@mail/js/emojis_mixin';
import EmojisFieldCommon from '@mail/views/fields/emojis_field_common';
import basicFields from 'web.basic_fields';
import registry from 'web.field_registry';
import { EmojisDropdown } from '@mail/js/emojis_dropdown';
import { EmojisFieldCommon } from '@mail/views/fields/emojis_field_common';
import { registry } from "@web/core/registry";
/**
* Extension of the FieldText that will add emojis support
*/
var EmojisTextField = basicFields.FieldText.extend(MailEmojisMixin, EmojisFieldCommon);
export class EmojisTextField extends TextField {
setup() {
super.setup();
this.targetEditElement = this.textareaRef;
this._setupOverride();
}
};
registry.add('text_emojis', EmojisTextField);
export default EmojisTextField;
patch(EmojisTextField.prototype, 'emojis_text_field_mail_mixin', MailEmojisMixin);
patch(EmojisTextField.prototype, 'emojis_text_field_field_mixin', EmojisFieldCommon);
EmojisTextField.template = 'mail.EmojisTextField';
EmojisTextField.components = { ...TextField.components, EmojisDropdown };
EmojisTextField.additionalClasses = [...(TextField.additionalClasses || []), 'o_field_text'];
registry.category("fields").add("text_emojis", EmojisTextField);
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="mail.EmojisTextField" t-inherit="web.TextField" t-inherit-mode="primary" owl="1">
<xpath expr="//span[1]" position="attributes">
<attribute name="t-ref">targetReadonlyElement</attribute>
</xpath>
<xpath expr="/*[last()]/*[last()]" position="after">
<EmojisDropdown onEmojiClick="onEmojiClick" type="'text'"
enable_emojis="props.enable_emojis" readonly="props.readonly"/>
</xpath>
</t>
</templates>
-11
View File
@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<div t-name="mail.legacy.Composer.emojis" class="o_mail_emoji_container">
<t t-foreach="emojis" t-as="emoji">
<span t-att-data-emoji="emoji.sources[0]" class="o_mail_emoji" t-att-title="emoji.name" t-att-aria-label="emoji.name">
<t t-out="emoji.codepoints"/>
</span>
</t>
</div>
</templates>
+19 -6
View File
@@ -1,7 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<div t-name="mail.EmojisDropdown" class="o_mail_emojis_dropdown o_mail_add_emoji dropdown position-relative">
<button class="btn btn-block dropdown-toggle px-3 py-1"
<t t-name="mail.EmojisDropdown" owl="1">
<div t-ref="emojisDropdown" t-att-class="{
'd-none': props.readonly || props.enable_emojis === false,
'o_mail_emojis_dropdown_translation': props.readonly and props.isTranslatable,
'o_mail_emojis_dropdown_textarea': props.type === 'text' and !props.readonly,
}" class="o_mail_emojis_dropdown o_mail_add_emoji dropdown position-relative">
<button class="btn btn-block dropdown-toggle px-3 py-1" t-ref="toggleRef"
type="button"
data-bs-toggle="dropdown"
aria-haspopup="true"
@@ -9,10 +14,18 @@
title="Add an emoji">
<i class="fa fa-smile-o"/>
</button>
<div class="dropdown-menu dropdown-menu-end border-0 p-2" style="width: 406px">
<t t-call="mail.legacy.Composer.emojis">
<t t-set="emojis" t-value="widget.emojis"></t>
</t>
<div class="dropdown-menu border-0 p-2" style="width: 406px">
<div class="o_mail_emoji_container">
<t t-foreach="emojis" t-as="emoji" t-key="emoji_index">
<span t-on-click="props.onEmojiClick"
t-att-data-emoji="emoji.sources[0]" class="o_mail_emoji"
t-att-title="emoji.name or emoji.description"
t-att-aria-label="emoji.name or emoji.description">
<t t-out="emoji.codepoints or emoji.unicode"/>
</span>
</t>
</div>
</div>
</div>
</t>
</templates>
@@ -44,7 +44,7 @@ odoo.define('mass_mailing.mass_mailing_editor_tour', function (require) {
trigger: '[name="body_arch"] iframe .o_editable h1',
run: () => {},
}, {
trigger: 'input[name="subject"]',
trigger: 'input#subject',
run: 'text Test',
}, {
trigger: '.o_form_view', // blur previous input
@@ -58,7 +58,7 @@
.o_form_view .o_mass_mailing_mailing_form,
.o_legacy_form_view.o_mass_mailing_mailing_form {
.o_mass_mailing_subject {
&.o_form_readonly .o_mass_mailing_subject {
// Place the favorite button without breaking the emoji widget
padding-right: 50px;
.o_mass_mailing_favorite {
@@ -68,7 +68,7 @@
margin-left: -40px;
bottom: 0;
}
span.o_field_char {
div.o_field_char_emojis {
// Override widget CSS attributes to place
// the favorite button next to the subject
margin-right: 1rem;