WebUI: Improve table scrolling and selection on mobile

This PR improves touch interaction with table rows that have a context menu. Previously, those rows couldn't be selected or scrolled on mobile. Additionally, this PR modifies the context menu to appear when the user removes their finger/touch, rather than the current behavior of appearing mid-touch. This allows us to only display the context menu if the user's finger remains on the same element, which should significantly reduce erroneous context menu triggering.

Closes #19819.
Closes #19820,
Closes #19823.
PR #20639.
This commit is contained in:
Thomas Piccirello 2024-04-08 23:33:10 -07:00 committed by GitHub
parent 01cc4ea90b
commit e697d40382
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 27 additions and 22 deletions

View file

@ -527,8 +527,10 @@ ul.filterList {
padding-left: 0; padding-left: 0;
} }
ul.filterList a { ul.filterList a,
ul.filterList span.link {
color: var(--color-text-default); color: var(--color-text-default);
cursor: pointer;
display: block; display: block;
overflow: hidden; overflow: hidden;
padding: 4px 6px; padding: 4px 6px;

View file

@ -442,9 +442,9 @@ window.addEventListener("DOMContentLoaded", function() {
margin_left = (category_path.length - 1) * 20; margin_left = (category_path.length - 1) * 20;
} }
const html = `<a href="#" style="margin-left: ${margin_left}px;" onclick="setCategoryFilter(${hash}); return false;">` const html = `<span class="link" href="#" style="margin-left: ${margin_left}px;" onclick="setCategoryFilter(${hash}); return false;">`
+ '<img src="images/view-categories.svg"/>' + '<img src="images/view-categories.svg"/>'
+ window.qBittorrent.Misc.escapeHtml(display_name) + ' (' + count + ')' + '</a>'; + window.qBittorrent.Misc.escapeHtml(display_name) + ' (' + count + ')' + '</span>';
const el = new Element('li', { const el = new Element('li', {
id: hash, id: hash,
html: html html: html
@ -524,9 +524,9 @@ window.addEventListener("DOMContentLoaded", function() {
tagFilterList.getChildren().each(c => c.destroy()); tagFilterList.getChildren().each(c => c.destroy());
const createLink = function(hash, text, count) { const createLink = function(hash, text, count) {
const html = `<a href="#" onclick="setTagFilter(${hash}); return false;">` const html = `<span class="link" href="#" onclick="setTagFilter(${hash}); return false;">`
+ '<img src="images/tags.svg"/>' + '<img src="images/tags.svg"/>'
+ window.qBittorrent.Misc.escapeHtml(text) + ' (' + count + ')' + '</a>'; + window.qBittorrent.Misc.escapeHtml(text) + ' (' + count + ')' + '</span>';
const el = new Element('li', { const el = new Element('li', {
id: hash, id: hash,
html: html html: html
@ -602,9 +602,9 @@ window.addEventListener("DOMContentLoaded", function() {
trackerFilterList.getChildren().each(c => c.destroy()); trackerFilterList.getChildren().each(c => c.destroy());
const createLink = function(hash, text, count) { const createLink = function(hash, text, count) {
const html = '<a href="#" onclick="setTrackerFilter(' + hash + ');return false;">' const html = '<span class="link" href="#" onclick="setTrackerFilter(' + hash + ');return false;">'
+ '<img src="images/trackers.svg"/>' + '<img src="images/trackers.svg"/>'
+ window.qBittorrent.Misc.escapeHtml(text.replace("%1", count)) + '</a>'; + window.qBittorrent.Misc.escapeHtml(text.replace("%1", count)) + '</span>';
const el = new Element('li', { const el = new Element('li', {
id: hash, id: hash,
html: html html: html

View file

@ -169,24 +169,30 @@ window.qBittorrent.ContextMenu = (function() {
}.bind(this)); }.bind(this));
elem.addEvent('touchstart', function(e) { elem.addEvent('touchstart', function(e) {
e.preventDefault();
clearTimeout(this.touchstartTimer);
this.hide(); this.hide();
this.touchStartAt = performance.now();
const touchstartEvent = e; this.touchStartEvent = e;
this.touchstartTimer = setTimeout(function() {
this.touchstartTimer = -1;
this.triggerMenu(touchstartEvent, elem);
}.bind(this), this.options.touchTimer);
}.bind(this)); }.bind(this));
elem.addEvent('touchend', function(e) { elem.addEvent('touchend', function(e) {
e.preventDefault(); const now = performance.now();
clearTimeout(this.touchstartTimer); const touchStartAt = this.touchStartAt;
this.touchstartTimer = -1; const touchStartEvent = this.touchStartEvent;
this.touchStartAt = null;
this.touchStartEvent = null;
const isTargetUnchanged = (Math.abs(e.event.pageX - touchStartEvent.event.pageX) <= 10) && (Math.abs(e.event.pageY - touchStartEvent.event.pageY) <= 10);
if (((now - touchStartAt) >= this.options.touchTimer) && isTargetUnchanged) {
this.triggerMenu(touchStartEvent, elem);
}
}.bind(this)); }.bind(this));
}, },
addTarget: function(t) { addTarget: function(t) {
// prevent long press from selecting this text
t.style.setProperty('user-select', 'none');
t.style.setProperty('-webkit-user-select', 'none');
this.targets[this.targets.length] = t; this.targets[this.targets.length] = t;
this.setupEventListeners(t); this.setupEventListeners(t);
}, },
@ -238,18 +244,16 @@ window.qBittorrent.ContextMenu = (function() {
lastShownContextMenu.hide(); lastShownContextMenu.hide();
this.fx.start(1); this.fx.start(1);
this.fireEvent('show'); this.fireEvent('show');
this.shown = true;
lastShownContextMenu = this; lastShownContextMenu = this;
return this; return this;
}, },
//hide the menu //hide the menu
hide: function(trigger) { hide: function(trigger) {
if (this.shown) { if (lastShownContextMenu && (lastShownContextMenu.menu.style.visibility !== 'hidden')) {
this.fx.start(0); this.fx.start(0);
//this.menu.fade('out'); //this.menu.fade('out');
this.fireEvent('hide'); this.fireEvent('hide');
this.shown = false;
} }
return this; return this;
}, },

View file

@ -775,7 +775,6 @@ window.qBittorrent.DynamicTable = (function() {
this._this.deselectAll(); this._this.deselectAll();
this._this.selectRow(this.rowId); this._this.selectRow(this.rowId);
} }
return false;
}); });
tr.addEvent('keydown', function(event) { tr.addEvent('keydown', function(event) {
switch (event.key) { switch (event.key) {