Refactor the emoji-picker somewhat
Trigger an `emojiSelected` event instead of manually calling `insertIntoTextArea` on the `converse-message-form` a component. This loosens the coupling between the emoji picker and `converse-message-form`. Call `disableArrowNavigation` when the emoji-picker is disconnected from the DOM or when escape is pressed. See #2754
This commit is contained in:
parent
1c0ce25f12
commit
e52056bb33
|
@ -15,9 +15,22 @@ export default class MessageForm extends ElementView {
|
|||
await this.model.initialized;
|
||||
this.listenTo(this.model.messages, 'change:correcting', this.onMessageCorrecting);
|
||||
this.listenTo(this.model, 'change:composing_spoiler', () => this.render());
|
||||
|
||||
this.handleEmojiSelection = ({ detail }) => this.insertIntoTextArea(
|
||||
detail.value,
|
||||
detail.autocompleting,
|
||||
false,
|
||||
detail.ac_position
|
||||
);
|
||||
document.addEventListener("emojiSelected", this.handleEmojiSelection);
|
||||
this.render();
|
||||
}
|
||||
|
||||
disconnectedCallback () {
|
||||
super.disconnectedCallback();
|
||||
document.removeEventListener("emojiSelected", this.handleEmojiSelection);
|
||||
}
|
||||
|
||||
toHTML () {
|
||||
return tpl_message_form(
|
||||
Object.assign(this.model.toJSON(), {
|
||||
|
|
|
@ -44,7 +44,7 @@ describe("Emojis", function () {
|
|||
expect(input.value).toBe(':grimacing:');
|
||||
|
||||
// Check that ENTER now inserts the match
|
||||
const enter_event = Object.assign({}, tab_event, {'keyCode': 13, 'key': 'Enter', 'target': input});
|
||||
const enter_event = Object.assign({}, tab_event, {'keyCode': 13, 'key': 'Enter', 'target': input, 'bubbles': true});
|
||||
input.dispatchEvent(new KeyboardEvent('keydown', enter_event));
|
||||
|
||||
await u.waitUntil(() => input.value === '');
|
||||
|
@ -148,7 +148,7 @@ describe("Emojis", function () {
|
|||
const input = picker.querySelector('.emoji-search');
|
||||
input.dispatchEvent(new KeyboardEvent('keydown', tab_event));
|
||||
await u.waitUntil(() => input.value === ':100:');
|
||||
const enter_event = Object.assign({}, tab_event, {'keyCode': 13, 'key': 'Enter', 'target': input});
|
||||
const enter_event = Object.assign({}, tab_event, {'keyCode': 13, 'key': 'Enter', 'target': input, 'bubbles': true});
|
||||
input.dispatchEvent(new KeyboardEvent('keydown', enter_event));
|
||||
expect(textarea.value).toBe(':100: ');
|
||||
|
||||
|
@ -193,7 +193,7 @@ describe("Emojis", function () {
|
|||
expect(visible_emojis[1].getAttribute('data-emoji')).toBe(':smiley_cat:');
|
||||
|
||||
// Check that pressing enter without an unambiguous match does nothing
|
||||
const enter_event = Object.assign({}, event, {'keyCode': 13});
|
||||
const enter_event = Object.assign({}, event, {'keyCode': 13, 'bubbles': true});
|
||||
input.dispatchEvent(new KeyboardEvent('keydown', enter_event));
|
||||
expect(input.value).toBe('smiley');
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ export default class EmojiDropdown extends DropdownBase {
|
|||
<converse-emoji-picker
|
||||
.chatview=${this.chatview}
|
||||
.model=${this.model}
|
||||
@emojiSelected=${() => this.hideMenu()}
|
||||
?render_emojis=${this.render_emojis}
|
||||
current_category="${this.model.get('current_category') || ''}"
|
||||
current_skintone="${this.model.get('current_skintone') || ''}"
|
||||
|
@ -95,12 +96,6 @@ export default class EmojiDropdown extends DropdownBase {
|
|||
super.showMenu();
|
||||
setTimeout(() => this.querySelector('.emoji-search')?.focus());
|
||||
}
|
||||
|
||||
hideMenu () {
|
||||
this.chatview.querySelector('converse-emoji-picker')?.disableArrowNavigation();
|
||||
super.hideMenu();
|
||||
setTimeout(() => this.chatview.querySelector('.chat-textarea')?.focus());
|
||||
}
|
||||
}
|
||||
|
||||
api.elements.define('converse-emoji-dropdown', EmojiDropdown);
|
||||
|
|
|
@ -3,6 +3,7 @@ import './emoji-dropdown.js';
|
|||
import DOMNavigator from "shared/dom-navigator";
|
||||
import debounce from 'lodash-es/debounce';
|
||||
import { CustomElement } from 'shared/components/element.js';
|
||||
import { KEYCODES } from '@converse/headless/shared/constants.js';
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
import { tpl_emoji_picker } from "./templates/emoji-picker.js";
|
||||
|
||||
|
@ -56,7 +57,7 @@ export default class EmojiPicker extends CustomElement {
|
|||
'onCategoryPicked': ev => this.chooseCategory(ev),
|
||||
'onSearchInputBlurred': ev => this.chatview.emitFocused(ev),
|
||||
'onSearchInputFocus': ev => this.onSearchInputFocus(ev),
|
||||
'onSearchInputKeyDown': ev => this.onKeyDown(ev),
|
||||
'onSearchInputKeyDown': ev => this.onSearchInputKeyDown(ev),
|
||||
'onSkintonePicked': ev => this.chooseSkinTone(ev),
|
||||
'query': this.query,
|
||||
'search_results': this.search_results,
|
||||
|
@ -120,6 +121,7 @@ export default class EmojiPicker extends CustomElement {
|
|||
disconnectedCallback() {
|
||||
const body = document.querySelector('body');
|
||||
body.removeEventListener('keydown', this.onGlobalKeyDown);
|
||||
this.disableArrowNavigation();
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
|
@ -127,14 +129,15 @@ export default class EmojiPicker extends CustomElement {
|
|||
if (!this.navigator) {
|
||||
return;
|
||||
}
|
||||
if (ev.keyCode === converse.keycodes.ENTER &&
|
||||
this.navigator.selected &&
|
||||
u.isVisible(this)) {
|
||||
if (ev.keyCode === KEYCODES.ENTER && u.isVisible(this)) {
|
||||
this.onEnterPressed(ev);
|
||||
} else if (ev.keyCode === converse.keycodes.DOWN_ARROW &&
|
||||
} else if (ev.keyCode === KEYCODES.DOWN_ARROW &&
|
||||
!this.navigator.enabled &&
|
||||
u.isVisible(this)) {
|
||||
this.enableArrowNavigation(ev);
|
||||
} else if (ev.keyCode === KEYCODES.ESCAPE) {
|
||||
this.disableArrowNavigation();
|
||||
setTimeout(() => this.chatview.querySelector('.chat-textarea').focus(), 50);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,8 +152,13 @@ export default class EmojiPicker extends CustomElement {
|
|||
insertIntoTextArea (value) {
|
||||
const autocompleting = this.model.get('autocompleting');
|
||||
const ac_position = this.model.get('ac_position');
|
||||
this.chatview.getMessageForm().insertIntoTextArea(value, autocompleting, false, ac_position);
|
||||
this.model.set({'autocompleting': null, 'query': '', 'ac_position': null});
|
||||
this.disableArrowNavigation();
|
||||
const options = {
|
||||
'bubbles': true,
|
||||
'detail': { value, autocompleting, ac_position }
|
||||
};
|
||||
this.dispatchEvent(new CustomEvent("emojiSelected", options));
|
||||
}
|
||||
|
||||
chooseSkinTone (ev) {
|
||||
|
@ -174,8 +182,8 @@ export default class EmojiPicker extends CustomElement {
|
|||
!this.navigator.enabled && this.navigator.enable();
|
||||
}
|
||||
|
||||
onKeyDown (ev) {
|
||||
if (ev.keyCode === converse.keycodes.TAB) {
|
||||
onSearchInputKeyDown (ev) {
|
||||
if (ev.keyCode === KEYCODES.TAB) {
|
||||
if (ev.target.value) {
|
||||
ev.preventDefault();
|
||||
const match = converse.emojis.shortnames.find(sn => _converse.FILTER_CONTAINS(sn, ev.target.value));
|
||||
|
@ -183,31 +191,19 @@ export default class EmojiPicker extends CustomElement {
|
|||
} else if (!this.navigator.enabled) {
|
||||
this.enableArrowNavigation(ev);
|
||||
}
|
||||
} else if (ev.keyCode === converse.keycodes.DOWN_ARROW && !this.navigator.enabled) {
|
||||
} else if (ev.keyCode === KEYCODES.DOWN_ARROW && !this.navigator.enabled) {
|
||||
this.enableArrowNavigation(ev);
|
||||
} else if (ev.keyCode === converse.keycodes.ENTER) {
|
||||
this.onEnterPressed(ev);
|
||||
} else if (ev.keyCode === converse.keycodes.ESCAPE) {
|
||||
u.ancestor(this, 'converse-emoji-dropdown').hideMenu();
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
} else if (
|
||||
ev.keyCode !== converse.keycodes.ENTER &&
|
||||
ev.keyCode !== converse.keycodes.DOWN_ARROW
|
||||
ev.keyCode !== KEYCODES.ENTER &&
|
||||
ev.keyCode !== KEYCODES.DOWN_ARROW
|
||||
) {
|
||||
this.debouncedFilter(ev.target);
|
||||
}
|
||||
}
|
||||
|
||||
onEnterPressed (ev) {
|
||||
if (ev.emoji_keypress_handled) {
|
||||
// Prevent the emoji from being inserted a 2nd time due to this
|
||||
// method being called by two event handlers: onKeyDown and _onGlobalKeyDown
|
||||
return;
|
||||
}
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
ev.emoji_keypress_handled = true;
|
||||
if (converse.emojis.shortnames.includes(ev.target.value)) {
|
||||
this.insertIntoTextArea(ev.target.value);
|
||||
} else if (this.search_results.length === 1) {
|
||||
|
@ -259,7 +255,7 @@ export default class EmojiPicker extends CustomElement {
|
|||
}
|
||||
|
||||
disableArrowNavigation () {
|
||||
this.navigator.disable();
|
||||
this.navigator?.disable();
|
||||
}
|
||||
|
||||
enableArrowNavigation (ev) {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import 'shared/components/icons.js';
|
||||
import DOMNavigator from "shared/dom-navigator.js";
|
||||
import { converse, api } from "@converse/headless/core";
|
||||
import DropdownBase from 'shared/components/dropdownbase.js';
|
||||
import { KEYCODES } from '@converse/headless/shared/constants.js';
|
||||
import { api } from "@converse/headless/core";
|
||||
import { html } from 'lit';
|
||||
import { until } from 'lit/directives/until.js';
|
||||
import DropdownBase from 'shared/components/dropdownbase.js';
|
||||
|
||||
import './styles/dropdown.scss';
|
||||
|
||||
|
@ -40,6 +41,17 @@ export default class Dropdown extends DropdownBase {
|
|||
this.initArrowNavigation();
|
||||
}
|
||||
|
||||
connectedCallback () {
|
||||
super.connectedCallback();
|
||||
this.hideOnEscape = ev => (ev.keyCode === KEYCODES.ESCAPE && this.hideMenu());
|
||||
document.addEventListener('keydown', this.hideOnEscape);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
document.removeEventListener('keydown', this.hideOnEscape);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
hideMenu () {
|
||||
super.hideMenu();
|
||||
this.navigator?.disable();
|
||||
|
@ -66,7 +78,7 @@ export default class Dropdown extends DropdownBase {
|
|||
|
||||
handleKeyUp (ev) {
|
||||
super.handleKeyUp(ev);
|
||||
if (ev.keyCode === converse.keycodes.DOWN_ARROW && !this.navigator.enabled) {
|
||||
if (ev.keyCode === KEYCODES.DOWN_ARROW && !this.navigator.enabled) {
|
||||
this.enableArrowNavigation(ev);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user