6 Commits
v0.4 ... v1.1

8 changed files with 257 additions and 9 deletions

View File

@@ -32,3 +32,40 @@
max-height: calc(100vh - var(--sv-shrink-offset, 240px)) !important; max-height: calc(100vh - var(--sv-shrink-offset, 240px)) !important;
min-height: 0 !important; min-height: 0 !important;
} }
/*
* Optional top horizontal scrollbar (added by Asset/js/top-scrollbar.js when the
* "Add a top horizontal scrollbar" setting is enabled). The inner element is given a
* width equal to the board's scrollable width by the script; overflow-x: auto then
* produces a scrollbar whose range mirrors the board's own bottom scrollbar. The 1px
* inner height keeps the bar to just the scrollbar itself.
*/
.sv-top-scroll {
overflow-x: auto;
overflow-y: hidden;
}
.sv-top-scroll-inner {
height: 1px;
}
/*
* Click-and-drag horizontal scrolling (added by Asset/js/drag-scroll.js when the
* "Drag the board background to scroll" setting is enabled). The .sv-drag-scroll class
* is set on #board-container by the script, so these rules are inert when disabled.
* A grab cursor advertises the empty background; cards keep the normal cursor since
* pressing them does a card drag, not a pan.
*/
#board-container.sv-drag-scroll {
cursor: grab;
}
#board-container.sv-drag-scroll .task-board {
cursor: default;
}
.sv-grabbing,
.sv-grabbing * {
cursor: grabbing !important;
user-select: none !important;
}

75
Asset/js/drag-scroll.js Normal file
View File

@@ -0,0 +1,75 @@
/*
* ShrinkVertically -- optional click-and-drag horizontal scrolling of the board.
*
* Like the Trello board canvas: press the empty board background and drag left/right
* to scroll #board-container horizontally. The pan only starts on empty background --
* pressing a task card, a drag handle, or any interactive control is ignored, so this
* never interferes with Kanboard's own card and column drag-and-drop.
*
* Loaded only when the "Drag the board background to scroll" setting is enabled.
*/
(function () {
"use strict";
// Pressing any of these (or their descendants) must NOT start a background pan.
var IGNORE = "a, button, input, textarea, select, label, " +
".task-board, [data-task-id], .task-board-sort-handle, " +
".draggable-row-handle, .dropdown, .ui-sortable-handle";
function isIgnored(el) {
return !!(el && el.closest && el.closest(IGNORE));
}
function init() {
var container = document.getElementById("board-container");
if (!container || container.getAttribute("data-sv-drag") === "1") {
return;
}
container.setAttribute("data-sv-drag", "1");
container.classList.add("sv-drag-scroll");
var dragging = false;
var startX = 0;
var startScrollLeft = 0;
container.addEventListener("mousedown", function (e) {
// Left button only, and only on empty board background.
if (e.button !== 0 || isIgnored(e.target)) {
return;
}
dragging = true;
startX = e.clientX;
startScrollLeft = container.scrollLeft;
container.classList.add("sv-grabbing");
e.preventDefault();
});
document.addEventListener("mousemove", function (e) {
if (!dragging) {
return;
}
container.scrollLeft = startScrollLeft - (e.clientX - startX);
e.preventDefault();
});
function stop() {
if (!dragging) {
return;
}
dragging = false;
container.classList.remove("sv-grabbing");
}
document.addEventListener("mouseup", stop);
document.addEventListener("mouseleave", stop);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();

68
Asset/js/top-scrollbar.js Normal file
View File

@@ -0,0 +1,68 @@
/*
* ShrinkVertically -- optional horizontal scrollbar above the board.
*
* Kanboard's board scrolls horizontally inside #board-container (overflow-x: auto),
* so the only horizontal scrollbar sits at the very bottom of the board. On a wide
* board you have to scroll all the way down to reach it. This adds a second, mirrored
* scrollbar just above the columns and keeps the two scroll positions in sync.
*
* Loaded only when the "Add a top horizontal scrollbar" setting is enabled.
*/
(function () {
"use strict";
function init() {
var container = document.getElementById("board-container");
// Only on the board, and never add it twice.
if (!container || document.querySelector(".sv-top-scroll")) {
return;
}
var bar = document.createElement("div");
bar.className = "sv-top-scroll";
var inner = document.createElement("div");
inner.className = "sv-top-scroll-inner";
bar.appendChild(inner);
container.parentNode.insertBefore(bar, container);
function sync() {
// Match the dummy bar's content width to the board's scrollable width
// so its scrollbar has the same range as the board's own scrollbar.
inner.style.width = container.scrollWidth + "px";
}
var lock = false;
bar.addEventListener("scroll", function () {
if (lock) { return; }
lock = true;
container.scrollLeft = bar.scrollLeft;
lock = false;
});
container.addEventListener("scroll", function () {
if (lock) { return; }
lock = true;
bar.scrollLeft = container.scrollLeft;
lock = false;
});
window.addEventListener("resize", sync);
// The board can be redrawn by the AJAX polling/refresh; keep the width current.
if (window.MutationObserver) {
new MutationObserver(sync).observe(container, { childList: true, subtree: true });
}
sync();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();

View File

@@ -17,11 +17,15 @@ class ConfigController extends BaseController
public function show() public function show()
{ {
$offset = (int) $this->configModel->get('shrink_vertically_offset', self::DEFAULT_OFFSET); $offset = (int) $this->configModel->get('shrink_vertically_offset', self::DEFAULT_OFFSET);
$topScrollbar = (int) $this->configModel->get('shrink_vertically_top_scrollbar', 0);
$dragScroll = (int) $this->configModel->get('shrink_vertically_drag_scroll', 0);
$this->response->html($this->helper->layout->config('shrinkVertically:config/show', array( $this->response->html($this->helper->layout->config('shrinkVertically:config/show', array(
'title' => t('Settings').' > '.t('Shrink Vertically'), 'title' => t('Settings').' > '.t('Shrink Vertically'),
'values' => array( 'values' => array(
'shrink_vertically_offset' => $offset, 'shrink_vertically_offset' => $offset,
'shrink_vertically_top_scrollbar' => $topScrollbar,
'shrink_vertically_drag_scroll' => $dragScroll,
), ),
'errors' => array(), 'errors' => array(),
))); )));
@@ -34,7 +38,14 @@ class ConfigController extends BaseController
$offset = isset($values['shrink_vertically_offset']) ? (int) $values['shrink_vertically_offset'] : self::DEFAULT_OFFSET; $offset = isset($values['shrink_vertically_offset']) ? (int) $values['shrink_vertically_offset'] : self::DEFAULT_OFFSET;
$offset = max(self::MIN_OFFSET, min(self::MAX_OFFSET, $offset)); $offset = max(self::MIN_OFFSET, min(self::MAX_OFFSET, $offset));
if ($this->configModel->save(array('shrink_vertically_offset' => $offset))) { $topScrollbar = isset($values['shrink_vertically_top_scrollbar']) ? 1 : 0;
$dragScroll = isset($values['shrink_vertically_drag_scroll']) ? 1 : 0;
if ($this->configModel->save(array(
'shrink_vertically_offset' => $offset,
'shrink_vertically_top_scrollbar' => $topScrollbar,
'shrink_vertically_drag_scroll' => $dragScroll,
))) {
$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.'));

View File

@@ -25,6 +25,20 @@ class Plugin extends Base
// Add a "Shrink Vertically" entry to the Settings sidebar. // Add a "Shrink Vertically" entry to the Settings sidebar.
$this->template->hook->attach('template:config:sidebar', 'shrinkVertically:config/sidebar'); $this->template->hook->attach('template:config:sidebar', 'shrinkVertically:config/sidebar');
// Optional: a second, mirrored horizontal scrollbar above the board columns.
if ((int) $this->configModel->get('shrink_vertically_top_scrollbar', 0) === 1) {
$this->hook->on('template:layout:js', array(
'template' => 'plugins/ShrinkVertically/Asset/js/top-scrollbar.js',
));
}
// Optional: click-and-drag the board background to scroll horizontally.
if ((int) $this->configModel->get('shrink_vertically_drag_scroll', 0) === 1) {
$this->hook->on('template:layout:js', array(
'template' => 'plugins/ShrinkVertically/Asset/js/drag-scroll.js',
));
}
} }
public function getPluginName() public function getPluginName()
@@ -44,7 +58,7 @@ class Plugin extends Base
public function getPluginVersion() public function getPluginVersion()
{ {
return '0.4.0'; return '1.1.0';
} }
public function getPluginHomepage() public function getPluginHomepage()

View File

@@ -1,7 +1,10 @@
# ShrinkVertically -- keep the board horizontal scrollbar visible # ShrinkVertically -- keep the board horizontal scrollbar visible
A tiny Kanboard plugin that fixes the vertically-collapsed board view so the bottom A small Kanboard plugin focused on getting around the board horizontally. It fixes the
horizontal scrollbar stays on screen. vertically-collapsed board view so the bottom scrollbar stays on screen, can optionally
add a second mirrored scrollbar above the columns, and can optionally let you drag the
board background to scroll (like Trello). Everything is configurable under
"Settings -> Shrink Vertically".
## The problem ## The problem
@@ -59,12 +62,38 @@ Copy this folder into your Kanboard installation as `plugins/ShrinkVertically/`.
directory name must be exactly `ShrinkVertically` (Kanboard derives the plugin namespace directory name must be exactly `ShrinkVertically` (Kanboard derives the plugin namespace
from the folder name). Reload the board; no build step, no configuration. from the folder name). Reload the board; no build step, no configuration.
## Top horizontal scrollbar (optional)
Kanboard's board scrolls horizontally inside `#board-container`, so its only horizontal
scrollbar is at the very bottom of the board -- on a wide board you must scroll all the
way down to move sideways. Enable "Add a top horizontal scrollbar" in the settings to get
a second, mirrored scrollbar just above the columns. A small script
(`Asset/js/top-scrollbar.js`, loaded only when the option is on) inserts a dummy scroll
strip above `#board-container` and keeps its scroll position in sync with the board both
ways.
## Drag to scroll (optional)
Kanboard has no way to pan the board by grabbing it; you must use the scrollbar. Enable
"Drag the board background to scroll" to click and drag on the empty board background to
scroll the columns sideways, like the Trello board canvas. A small script
(`Asset/js/drag-scroll.js`, loaded only when the option is on) starts a horizontal pan on
`#board-container` only when the press lands on empty background -- pressing a task card,
a drag handle or any control is ignored, so Kanboard's own card and column drag-and-drop
keep working unchanged.
## Settings ## Settings
Go to "Settings -> Shrink Vertically" to set the pixel offset. The value is stored in the Go to "Settings -> Shrink Vertically":
Kanboard configuration and injected in the page head as the `--sv-shrink-offset` CSS
- **Vertical offset in pixels** -- pixels reserved around the collapsed columns. Stored in
the Kanboard configuration and injected in the page head as the `--sv-shrink-offset` CSS
variable. Increase it if the scrollbar is still clipped (for example with a taller theme variable. Increase it if the scrollbar is still clipped (for example with a taller theme
header); decrease it to make the collapsed columns taller. Default: 240. header); decrease it to make the collapsed columns taller. Default: 240.
- **Add a top horizontal scrollbar to the board** -- toggles the mirrored top scrollbar
described above.
- **Drag the board background to scroll horizontally** -- toggles the Trello-style
click-and-drag panning described above.
## License ## License

View File

@@ -13,6 +13,20 @@
</p> </p>
</fieldset> </fieldset>
<fieldset>
<?= $this->form->checkbox('shrink_vertically_top_scrollbar', t('Add a top horizontal scrollbar to the board'), 1, isset($values['shrink_vertically_top_scrollbar']) && $values['shrink_vertically_top_scrollbar'] == 1) ?>
<p class="form-help">
<?= t('Adds a second horizontal scrollbar above the columns, mirrored with the board scrollbar, so you do not have to scroll to the bottom to move sideways.') ?>
</p>
</fieldset>
<fieldset>
<?= $this->form->checkbox('shrink_vertically_drag_scroll', t('Drag the board background to scroll horizontally'), 1, isset($values['shrink_vertically_drag_scroll']) && $values['shrink_vertically_drag_scroll'] == 1) ?>
<p class="form-help">
<?= t('Like Trello: click and drag on the empty board background to scroll the columns sideways. Dragging a task card or a handle still works as usual.') ?>
</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>

View File

@@ -1 +1 @@
ShrinkVertically v0.4 ShrinkVertically v1.1