Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ff6abf1d49 | |||
| 310c34620f | |||
| a5721890a6 |
35
Asset/js/click-guard.js
Normal file
35
Asset/js/click-guard.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* OrganonTweaks -- only open a card on a quick click.
|
||||||
|
*
|
||||||
|
* A real click is a quick press-release; a drag is a press, some movement, and a release
|
||||||
|
* much later. When you drop a card, jQuery UI can leak a trailing "click" on it, and
|
||||||
|
* Kanboard opens the card -- an unwanted side effect of the drag.
|
||||||
|
*
|
||||||
|
* We simply time the press on a card: record the moment of mousedown / touchstart, and on
|
||||||
|
* the click that follows, if more than THRESHOLD ms elapsed the press was a drag (held),
|
||||||
|
* not a click, so we swallow the click in the capture phase before Kanboard's open handler
|
||||||
|
* runs. A normal click is well under the threshold and opens the card as usual.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var THRESHOLD = 100; // ms; a press on a card held longer than this is a drag, not a click
|
||||||
|
var pressStart = 0;
|
||||||
|
|
||||||
|
function onDown(e) {
|
||||||
|
pressStart = (e.target && e.target.closest && e.target.closest(".task-board")) ? Date.now() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("mousedown", onDown, true);
|
||||||
|
document.addEventListener("touchstart", onDown, true);
|
||||||
|
|
||||||
|
document.addEventListener("click", function (e) {
|
||||||
|
if (pressStart &&
|
||||||
|
e.target && e.target.closest && e.target.closest(".task-board") &&
|
||||||
|
(Date.now() - pressStart) > THRESHOLD) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
pressStart = 0;
|
||||||
|
}, true);
|
||||||
|
})();
|
||||||
@@ -53,19 +53,18 @@
|
|||||||
|
|
||||||
// Native comment icon sits just before the description icon; if there is no
|
// Native comment icon sits just before the description icon; if there is no
|
||||||
// description, before the age / closed block. That is the slot we move into.
|
// description, before the age / closed block. That is the slot we move into.
|
||||||
var anchor = null;
|
// NB: the description icon is wrapped in <span class="tooltip"> (Kanboard's
|
||||||
var descIcon = row.querySelector("i.fa-file-text-o");
|
// tooltipLink), not an <a>, so we walk up to whatever is the row's direct child.
|
||||||
|
var anchor = row.querySelector("i.fa-file-text-o") ||
|
||||||
|
row.querySelector(".task-icon-age") ||
|
||||||
|
row.querySelector(".task-board-closed");
|
||||||
|
|
||||||
if (descIcon) {
|
while (anchor && anchor.parentNode !== row) {
|
||||||
anchor = descIcon.closest("a");
|
anchor = anchor.parentNode;
|
||||||
}
|
|
||||||
|
|
||||||
if (!anchor) {
|
|
||||||
anchor = row.querySelector(".task-icon-age") || row.querySelector(".task-board-closed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move only if needed (keeps the MutationObserver from looping).
|
// Move only if needed (keeps the MutationObserver from looping).
|
||||||
if (anchor && anchor.parentNode === row && item.nextElementSibling !== anchor) {
|
if (anchor && anchor !== item && item.nextElementSibling !== anchor) {
|
||||||
row.insertBefore(item, anchor);
|
row.insertBefore(item, anchor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,16 +76,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
if (!document.getElementById("board")) {
|
var container = document.getElementById("board-container");
|
||||||
|
if (!container) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply();
|
apply();
|
||||||
|
|
||||||
var container = document.getElementById("board-container");
|
// Observe the STABLE parent, not #board-container: Kanboard replaces the container
|
||||||
|
// on every card drop / AJAX poll (BoardDragAndDrop.refresh), which would kill an
|
||||||
|
// observer bound to the container itself and leave the items un-relocated after a
|
||||||
|
// move. The parent survives, so apply() keeps running on every rebuild.
|
||||||
|
var parent = container.parentNode;
|
||||||
|
|
||||||
if (container && window.MutationObserver) {
|
if (parent && window.MutationObserver) {
|
||||||
new MutationObserver(apply).observe(container, { childList: true, subtree: true });
|
new MutationObserver(apply).observe(parent, { childList: true, subtree: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class ConfigController extends BaseController
|
|||||||
'values' => array(
|
'values' => array(
|
||||||
'organon_tweaks_always_comment_icon' => (int) $this->configModel->get('organon_tweaks_always_comment_icon', 1),
|
'organon_tweaks_always_comment_icon' => (int) $this->configModel->get('organon_tweaks_always_comment_icon', 1),
|
||||||
'organon_tweaks_keep_scroll' => (int) $this->configModel->get('organon_tweaks_keep_scroll', 1),
|
'organon_tweaks_keep_scroll' => (int) $this->configModel->get('organon_tweaks_keep_scroll', 1),
|
||||||
|
'organon_tweaks_quick_click_only' => (int) $this->configModel->get('organon_tweaks_quick_click_only', 1),
|
||||||
),
|
),
|
||||||
'errors' => array(),
|
'errors' => array(),
|
||||||
)));
|
)));
|
||||||
@@ -27,10 +28,12 @@ class ConfigController extends BaseController
|
|||||||
|
|
||||||
$alwaysCommentIcon = isset($values['organon_tweaks_always_comment_icon']) ? 1 : 0;
|
$alwaysCommentIcon = isset($values['organon_tweaks_always_comment_icon']) ? 1 : 0;
|
||||||
$keepScroll = isset($values['organon_tweaks_keep_scroll']) ? 1 : 0;
|
$keepScroll = isset($values['organon_tweaks_keep_scroll']) ? 1 : 0;
|
||||||
|
$quickClickOnly = isset($values['organon_tweaks_quick_click_only']) ? 1 : 0;
|
||||||
|
|
||||||
if ($this->configModel->save(array(
|
if ($this->configModel->save(array(
|
||||||
'organon_tweaks_always_comment_icon' => $alwaysCommentIcon,
|
'organon_tweaks_always_comment_icon' => $alwaysCommentIcon,
|
||||||
'organon_tweaks_keep_scroll' => $keepScroll,
|
'organon_tweaks_keep_scroll' => $keepScroll,
|
||||||
|
'organon_tweaks_quick_click_only' => $quickClickOnly,
|
||||||
))) {
|
))) {
|
||||||
$this->flash->success(t('Settings saved successfully.'));
|
$this->flash->success(t('Settings saved successfully.'));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
10
Plugin.php
10
Plugin.php
@@ -34,6 +34,14 @@ class Plugin extends Base
|
|||||||
'template' => 'plugins/OrganonTweaks/Asset/js/keep-scroll.js',
|
'template' => 'plugins/OrganonTweaks/Asset/js/keep-scroll.js',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tweak: only open a card on a quick click (a press held > 100ms is a drag, so the
|
||||||
|
// trailing click is swallowed and the card does not open).
|
||||||
|
if ((int) $this->configModel->get('organon_tweaks_quick_click_only', 1) === 1) {
|
||||||
|
$this->hook->on('template:layout:js', array(
|
||||||
|
'template' => 'plugins/OrganonTweaks/Asset/js/click-guard.js',
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHelpers()
|
public function getHelpers()
|
||||||
@@ -60,7 +68,7 @@ class Plugin extends Base
|
|||||||
|
|
||||||
public function getPluginVersion()
|
public function getPluginVersion()
|
||||||
{
|
{
|
||||||
return '1.0.0';
|
return '1.2.1';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPluginHomepage()
|
public function getPluginHomepage()
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -47,12 +47,24 @@ board stays where you were.
|
|||||||
itself is replaced), reads/writes `scrollLeft` on whichever `#board-container` is current.
|
itself is replaced), reads/writes `scrollLeft` on whichever `#board-container` is current.
|
||||||
- **On by default.** Toggle it under "Settings -> Organon Tweaks".
|
- **On by default.** Toggle it under "Settings -> Organon Tweaks".
|
||||||
|
|
||||||
|
### Open a card only on a quick click
|
||||||
|
|
||||||
|
Dropping a card sometimes leaks a trailing "click" that opens the card -- an unwanted side
|
||||||
|
effect of the drag. A real click is a quick press-release; a drag is a press held much
|
||||||
|
longer. So `Asset/js/click-guard.js` times the press on a card (`mousedown`/`touchstart` to
|
||||||
|
the following `click`) and, if it was held longer than **100 ms**, swallows the click in the
|
||||||
|
capture phase before Kanboard's open handler -- so a drag never opens the card. Normal
|
||||||
|
clicks (well under 100 ms) still open cards as usual. The 100 ms threshold is fixed.
|
||||||
|
|
||||||
|
- **On by default.** Toggle it under "Settings -> Organon Tweaks".
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
Global (per Kanboard instance) and admin-only, under "Settings -> Organon Tweaks":
|
Global (per Kanboard instance) and admin-only, under "Settings -> Organon Tweaks":
|
||||||
|
|
||||||
- **Always show the comment icon on board cards** -- default on.
|
- **Always show the comment icon on board cards** -- default on.
|
||||||
- **Keep the board scroll position across refreshes** -- default on.
|
- **Keep the board scroll position across refreshes** -- default on.
|
||||||
|
- **Open a card only on a quick click** -- default on.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,13 @@
|
|||||||
</p>
|
</p>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<?= $this->form->checkbox('organon_tweaks_quick_click_only', t('Open a card only on a quick click'), 1, isset($values['organon_tweaks_quick_click_only']) && $values['organon_tweaks_quick_click_only'] == 1) ?>
|
||||||
|
<p class="form-help">
|
||||||
|
<?= t('A press on a card held longer than 100 ms is treated as a drag, so releasing it does not open the card. Fixes a drag sometimes opening the card on release. Normal clicks are well under 100 ms.') ?>
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
|
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user