From d1e1a0082d9555cca3e453890932ca01f0e85156 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 13 Mar 2026 14:42:48 -0400 Subject: [PATCH] fix: long-press support for topicSelect, #14045 --- public/src/client/category/tools.js | 9 ++++-- public/src/modules/topicSelect.js | 46 +++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/public/src/client/category/tools.js b/public/src/client/category/tools.js index 58a257f293..bd15ff459b 100644 --- a/public/src/client/category/tools.js +++ b/public/src/client/category/tools.js @@ -9,11 +9,16 @@ define('forum/category/tools', [ 'api', 'bootbox', 'alerts', -], function (topicSelect, threadTools, components, api, bootbox, alerts) { + 'bootstrap', +], function (topicSelect, threadTools, components, api, bootbox, alerts, bootstrap) { const CategoryTools = {}; CategoryTools.init = function (containerEl) { - topicSelect.init(updateDropdownOptions, containerEl); + topicSelect.init(updateDropdownOptions, () => { + const toggleEl = document.querySelector('.thread-tools button'); + const dropdown = new bootstrap.Dropdown(toggleEl); + dropdown.show(); + }, containerEl); handlePinnedTopicSort(); diff --git a/public/src/modules/topicSelect.js b/public/src/modules/topicSelect.js index 50d38c4ba7..54cffc4465 100644 --- a/public/src/modules/topicSelect.js +++ b/public/src/modules/topicSelect.js @@ -7,13 +7,14 @@ define('topicSelect', ['components'], function (components) { let topicsContainer; - TopicSelect.init = function (onSelect, containerEl) { + TopicSelect.init = function (onSelect, onLongPress, containerEl) { topicsContainer = containerEl || $('[component="category"]'); topicsContainer.on('selectstart', '[component="topic/select"]', function (ev) { ev.preventDefault(); }); - topicsContainer.on('click', '[component="topic/select"]', function (ev) { + let isLongPress = false; + const click = function (ev) { const select = $(this); const topicEl = select.parents('[component="category/topic"]'); if (ev.shiftKey) { @@ -28,6 +29,47 @@ define('topicSelect', ['components'], function (components) { if (typeof onSelect === 'function') { onSelect(); } + }; + topicsContainer.on('click', '[component="topic/select"]', function (ev) { + if (isLongPress) { + ev.preventDefault(); + ev.stopImmediatePropagation(); + return false; + } + + click.call(this, ev); + }); + + // Long press + let longPressTimeout; + const start = function (ev) { + if (ev.type === 'touchstart') { + ev.preventDefault(); + } + isLongPress = false; + longPressTimeout = setTimeout(() => { + isLongPress = true; + click.call(this, ev); + if (navigator.vibrate) { + navigator.vibrate(50); + } + const topicEl = this.closest('[component="category/topic"]'); + if (topicEl.classList.contains('selected')) { + onLongPress(); + } + }, 500); + }; + const cancel = () => { + clearTimeout(longPressTimeout); + }; + topicsContainer.on('mousedown', '[component="topic/select"]', start); + topicsContainer.on('touchstart', '[component="topic/select"]', start); + topicsContainer.on('mouseup', '[component="topic/select"]', cancel); + topicsContainer.on('mouseleave', '[component="topic/select"]', cancel); + topicsContainer.on('touchend', '[component="topic/select"]', cancel); + topicsContainer.on('touchcancel', '[component="topic/select"]', cancel); + topicsContainer.on('contextmenu', (e) => { + e.preventDefault(); }); };