keep board horizontal scroll across refresh: restore scrollLeft after #board-container rebuild; v1.0
This commit is contained in:
55
Asset/js/keep-scroll.js
Normal file
55
Asset/js/keep-scroll.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* OrganonTweaks -- keep the board's horizontal scroll position across refreshes.
|
||||||
|
*
|
||||||
|
* When you drop a card (and on Kanboard's periodic AJAX polling) the board is rebuilt with
|
||||||
|
* `$("#board-container").replaceWith(data)` (BoardDragAndDrop.refresh). The brand-new
|
||||||
|
* element starts at scrollLeft 0, so the board snaps back to the first column -- a jarring
|
||||||
|
* jump. This remembers the last scroll position and restores it the moment a replacement
|
||||||
|
* container appears, so the view stays put.
|
||||||
|
*
|
||||||
|
* We observe the STABLE parent (the container itself is replaced) and read the scroll from
|
||||||
|
* whichever #board-container is current, so it keeps working after every rebuild.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
var container = document.getElementById("board-container");
|
||||||
|
if (!container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parent = container.parentNode;
|
||||||
|
var lastScrollLeft = container.scrollLeft;
|
||||||
|
var lastContainer = container;
|
||||||
|
|
||||||
|
// Remember the position whenever the board is scrolled (capture: scroll does not
|
||||||
|
// bubble, and this survives the container being replaced).
|
||||||
|
document.addEventListener("scroll", function (e) {
|
||||||
|
var c = document.getElementById("board-container");
|
||||||
|
if (c && e.target === c) {
|
||||||
|
lastScrollLeft = c.scrollLeft;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
// When the board is rebuilt, #board-container becomes a new element at scrollLeft 0;
|
||||||
|
// restore the remembered position before the browser paints it.
|
||||||
|
if (window.MutationObserver) {
|
||||||
|
new MutationObserver(function () {
|
||||||
|
var c = document.getElementById("board-container");
|
||||||
|
if (c && c !== lastContainer) {
|
||||||
|
lastContainer = c;
|
||||||
|
if (c.scrollLeft !== lastScrollLeft) {
|
||||||
|
c.scrollLeft = lastScrollLeft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).observe(parent, { childList: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -15,6 +15,7 @@ class ConfigController extends BaseController
|
|||||||
'title' => t('Settings').' > '.t('Organon Tweaks'),
|
'title' => t('Settings').' > '.t('Organon Tweaks'),
|
||||||
'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),
|
||||||
),
|
),
|
||||||
'errors' => array(),
|
'errors' => array(),
|
||||||
)));
|
)));
|
||||||
@@ -25,8 +26,12 @@ class ConfigController extends BaseController
|
|||||||
$values = $this->request->getValues();
|
$values = $this->request->getValues();
|
||||||
|
|
||||||
$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;
|
||||||
|
|
||||||
if ($this->configModel->save(array('organon_tweaks_always_comment_icon' => $alwaysCommentIcon))) {
|
if ($this->configModel->save(array(
|
||||||
|
'organon_tweaks_always_comment_icon' => $alwaysCommentIcon,
|
||||||
|
'organon_tweaks_keep_scroll' => $keepScroll,
|
||||||
|
))) {
|
||||||
$this->flash->success(t('Settings saved successfully.'));
|
$this->flash->success(t('Settings saved successfully.'));
|
||||||
} else {
|
} else {
|
||||||
$this->flash->failure(t('Unable to save your settings.'));
|
$this->flash->failure(t('Unable to save your settings.'));
|
||||||
|
|||||||
10
Plugin.php
10
Plugin.php
@@ -26,6 +26,14 @@ class Plugin extends Base
|
|||||||
if ((int) $this->configModel->get('organon_tweaks_always_comment_icon', 1) === 1) {
|
if ((int) $this->configModel->get('organon_tweaks_always_comment_icon', 1) === 1) {
|
||||||
$this->template->hook->attach('template:board:task:icons', 'organonTweaks:board/task_comment_icon');
|
$this->template->hook->attach('template:board:task:icons', 'organonTweaks:board/task_comment_icon');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tweak: keep the board horizontal scroll position across refreshes (Kanboard
|
||||||
|
// rebuilds #board-container on card drop / polling, resetting scrollLeft to 0).
|
||||||
|
if ((int) $this->configModel->get('organon_tweaks_keep_scroll', 1) === 1) {
|
||||||
|
$this->hook->on('template:layout:js', array(
|
||||||
|
'template' => 'plugins/OrganonTweaks/Asset/js/keep-scroll.js',
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getHelpers()
|
public function getHelpers()
|
||||||
@@ -52,7 +60,7 @@ class Plugin extends Base
|
|||||||
|
|
||||||
public function getPluginVersion()
|
public function getPluginVersion()
|
||||||
{
|
{
|
||||||
return '0.3.1';
|
return '1.0.0';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPluginHomepage()
|
public function getPluginHomepage()
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -35,11 +35,24 @@ have comments keep showing the count as before.
|
|||||||
read-only viewers).
|
read-only viewers).
|
||||||
- **On by default.** Toggle it under "Settings -> Organon Tweaks".
|
- **On by default.** Toggle it under "Settings -> Organon Tweaks".
|
||||||
|
|
||||||
|
### Keep the board scroll position across refreshes
|
||||||
|
|
||||||
|
When you drop a card (or Kanboard auto-refreshes the board via polling), it rebuilds the
|
||||||
|
board with `$("#board-container").replaceWith(...)`, and the new element starts scrolled to
|
||||||
|
the far left -- so the view jumps back to the first column. This tweak remembers the
|
||||||
|
horizontal scroll position and restores it as soon as the rebuilt board appears, so the
|
||||||
|
board stays where you were.
|
||||||
|
|
||||||
|
- Implemented in `Asset/js/keep-scroll.js`: it observes the stable parent (the container
|
||||||
|
itself is replaced), reads/writes `scrollLeft` on whichever `#board-container` is current.
|
||||||
|
- **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.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,13 @@
|
|||||||
</p>
|
</p>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<?= $this->form->checkbox('organon_tweaks_keep_scroll', t('Keep the board scroll position across refreshes'), 1, isset($values['organon_tweaks_keep_scroll']) && $values['organon_tweaks_keep_scroll'] == 1) ?>
|
||||||
|
<p class="form-help">
|
||||||
|
<?= t('When a card is dropped (or the board auto-refreshes), Kanboard rebuilds the board and jumps back to the first column. This restores the horizontal scroll position so the board stays where you were.') ?>
|
||||||
|
</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