typeahead: Ignore mouse position for selection until it's moved.

Added a property `mouse_moved_since_typeahead` to the typeahead class
which tracks whether the mouse has been moved since the typeahead
menu appeared.

The hovered over menu item is highlighted on `mouseenter` only if
`mouseMoved` is true. Otherwise, the cursor is hidden temporarily.

Code substantially reorganized by tabbott.

Fixes: #21018.
This commit is contained in:
N-Shar-ma
2022-02-06 23:51:17 +05:30
committed by Tim Abbott
parent 653af70d5a
commit 728fee31b2
3 changed files with 33 additions and 1 deletions

View File

@@ -491,6 +491,11 @@ class CommonUtils {
`//*[@class="typeahead dropdown-menu" and contains(@style, "display: block")]//li[contains(normalize-space(), "${item}")]//a`, `//*[@class="typeahead dropdown-menu" and contains(@style, "display: block")]//li[contains(normalize-space(), "${item}")]//a`,
{visible: true}, {visible: true},
); );
const entry_x = (await entry?.boundingBox())?.x;
const entry_y = (await entry?.boundingBox())?.y;
if (entry_x && entry_y) {
await page.mouse.move(entry_x, entry_y);
}
await page.evaluate((entry) => { await page.evaluate((entry) => {
entry.click(); entry.click();
}, entry); }, entry);

View File

@@ -229,6 +229,9 @@ async function get_suggestions(page: Page, str: string): Promise<void> {
async function select_from_suggestions(page: Page, item: string): Promise<void> { async function select_from_suggestions(page: Page, item: string): Promise<void> {
await page.evaluate((item: string) => { await page.evaluate((item: string) => {
const tah = $(".create_default_stream").data().typeahead; const tah = $(".create_default_stream").data().typeahead;
tah.mousemove({
currentTarget: $(`.typeahead:visible li:contains("${CSS.escape(item)}")`)[0],
});
tah.mouseenter({ tah.mouseenter({
currentTarget: $(`.typeahead:visible li:contains("${CSS.escape(item)}")`)[0], currentTarget: $(`.typeahead:visible li:contains("${CSS.escape(item)}")`)[0],
}); });

View File

@@ -96,6 +96,7 @@
this.$header = $(this.options.header_html).appendTo(this.$container) this.$header = $(this.options.header_html).appendTo(this.$container)
this.source = this.options.source this.source = this.options.source
this.shown = false this.shown = false
this.mouse_moved_since_typeahead = false
this.dropup = this.options.dropup this.dropup = this.options.dropup
this.fixed = this.options.fixed || false; this.fixed = this.options.fixed || false;
this.automated = this.options.automated || this.automated; this.automated = this.options.automated || this.automated;
@@ -199,6 +200,7 @@
this.$container.show() this.$container.show()
this.shown = true this.shown = true
this.mouse_moved_since_typeahead = false
return this return this
} }
@@ -328,11 +330,12 @@
this.$menu this.$menu
.on('click', 'li', this.click.bind(this)) .on('click', 'li', this.click.bind(this))
.on('mouseenter', 'li', this.mouseenter.bind(this)) .on('mouseenter', 'li', this.mouseenter.bind(this))
.on('mousemove', 'li', this.mousemove.bind(this))
} }
, unlisten: function () { , unlisten: function () {
this.$container.remove(); this.$container.remove();
var events = ["blur", "keydown", "keyup", "keypress"]; var events = ["blur", "keydown", "keyup", "keypress", "mousemove"];
for (var i=0; i<events.length; i++) { for (var i=0; i<events.length; i++) {
this.$element.off(events[i]); this.$element.off(events[i]);
} }
@@ -375,6 +378,15 @@
} }
} }
, mousemove: function(e) {
if (!this.mouse_moved_since_typeahead) {
/* Undo cursor disabling in mouseenter handler. */
$(e.currentTarget).find('a').css('cursor', '');
this.mouse_moved_since_typeahead = true;
this.mouseenter(e)
}
}
, keydown: function (e) { , keydown: function (e) {
if (this.trigger_selection(e)) { if (this.trigger_selection(e)) {
if (!this.shown) return; if (!this.shown) return;
@@ -450,6 +462,18 @@
} }
, mouseenter: function (e) { , mouseenter: function (e) {
if (!this.mouse_moved_since_typeahead) {
// Prevent the annoying interaction where your mouse happens
// to be in the space where typeahead will open. (This would
// result in the mouse taking priority over the keyboard for
// what you selected). If the mouse has not been moved since
// the appearance of the typeahead menu, we disable the
// cursor, which in turn prevents the currently hovered
// element from being selected. The mousemove handler
// overrides this logic.
$(e.currentTarget).find('a').css('cursor', 'none')
return
}
this.$menu.find('.active').removeClass('active') this.$menu.find('.active').removeClass('active')
$(e.currentTarget).addClass('active') $(e.currentTarget).addClass('active')
} }