6 Commits
v0.3 ... v1.0

9 changed files with 220 additions and 13 deletions

View File

@@ -13,8 +13,10 @@
* breathing room). A fixed-px budget is more reliable than a proportional vh value * breathing room). A fixed-px budget is more reliable than a proportional vh value
* because that chrome is a fixed height regardless of screen size. * because that chrome is a fixed height regardless of screen size.
* *
* Tune the single number below (240px) if the scrollbar is still clipped or if you * The pixel budget is the --sv-shrink-offset CSS variable. It is injected in the
* want the columns taller. * page head from the plugin setting (Settings -> Shrink Vertically); when unset it
* falls back to 240px below. Increase it if the scrollbar is still clipped, or
* decrease it to make the columns taller.
* *
* The selector is intentionally more specific than core's single-class rule, and * The selector is intentionally more specific than core's single-class rule, and
* uses !important, so this override wins even alongside a theme (e.g. Essential). * uses !important, so this override wins even alongside a theme (e.g. Essential).
@@ -27,6 +29,22 @@
* (non-important) min-height, so we reset it to 0 and let max-height win. * (non-important) min-height, so we reset it to 0 and let max-height win.
*/ */
#board td .board-task-list.board-task-list-compact { #board td .board-task-list.board-task-list-compact {
max-height: calc(100vh - 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;
}

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

@@ -0,0 +1,52 @@
<?php
namespace Kanboard\Plugin\ShrinkVertically\Controller;
use Kanboard\Controller\BaseController;
/**
* Settings page for the ShrinkVertically plugin: configure how many pixels are
* reserved around the vertically collapsed board columns.
*/
class ConfigController extends BaseController
{
const DEFAULT_OFFSET = 240;
const MIN_OFFSET = 0;
const MAX_OFFSET = 2000;
public function show()
{
$offset = (int) $this->configModel->get('shrink_vertically_offset', self::DEFAULT_OFFSET);
$topScrollbar = (int) $this->configModel->get('shrink_vertically_top_scrollbar', 0);
$this->response->html($this->helper->layout->config('shrinkVertically:config/show', array(
'title' => t('Settings').' &gt; '.t('Shrink Vertically'),
'values' => array(
'shrink_vertically_offset' => $offset,
'shrink_vertically_top_scrollbar' => $topScrollbar,
),
'errors' => array(),
)));
}
public function save()
{
$values = $this->request->getValues();
$offset = isset($values['shrink_vertically_offset']) ? (int) $values['shrink_vertically_offset'] : self::DEFAULT_OFFSET;
$offset = max(self::MIN_OFFSET, min(self::MAX_OFFSET, $offset));
$topScrollbar = isset($values['shrink_vertically_top_scrollbar']) ? 1 : 0;
if ($this->configModel->save(array(
'shrink_vertically_offset' => $offset,
'shrink_vertically_top_scrollbar' => $topScrollbar,
))) {
$this->flash->success(t('Settings saved successfully.'));
} else {
$this->flash->failure(t('Unable to save your settings.'));
}
$this->response->redirect($this->helper->url->to('ConfigController', 'show', array('plugin' => 'ShrinkVertically')));
}
}

View File

@@ -14,11 +14,24 @@ class Plugin extends Base
'template' => 'plugins/ShrinkVertically/Asset/css/shrink-vertically.css', 'template' => 'plugins/ShrinkVertically/Asset/css/shrink-vertically.css',
)); ));
// Inject the configured pixel offset as a CSS variable in the page head.
$this->template->hook->attach('template:layout:head', 'shrinkVertically:layout/variable');
// Append a "+" to the two vertical-collapse menu labels so it is visible // Append a "+" to the two vertical-collapse menu labels so it is visible
// that the behaviour is modified by this plugin. // that the behaviour is modified by this plugin.
$this->hook->on('template:layout:js', array( $this->hook->on('template:layout:js', array(
'template' => 'plugins/ShrinkVertically/Asset/js/shrink-vertically.js', 'template' => 'plugins/ShrinkVertically/Asset/js/shrink-vertically.js',
)); ));
// Add a "Shrink Vertically" entry to the Settings 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',
));
}
} }
public function getPluginName() public function getPluginName()
@@ -38,7 +51,7 @@ class Plugin extends Base
public function getPluginVersion() public function getPluginVersion()
{ {
return '0.1.0'; return '1.0.0';
} }
public function getPluginHomepage() public function getPluginHomepage()

View File

@@ -1,7 +1,9 @@
# 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 the board's horizontal scrollbar. It fixes the
horizontal scrollbar stays on screen. vertically-collapsed board view so the bottom scrollbar stays on screen, and can
optionally add a second, mirrored horizontal scrollbar above the columns. Both are
configurable under "Settings -> Shrink Vertically".
## The problem ## The problem
@@ -25,13 +27,19 @@ list:
```css ```css
#board td .board-task-list.board-task-list-compact { #board td .board-task-list.board-task-list-compact {
max-height: calc(100vh - 240px) !important; max-height: calc(100vh - var(--sv-shrink-offset, 240px)) !important;
min-height: 0 !important;
} }
``` ```
The `240px` reserves room for the page header, the board column header, the horizontal The offset reserves room for the page header, the board column header, the horizontal
scrollbar, and a little breathing space. A fixed-pixel budget is more reliable than a scrollbar, and a little breathing space. A fixed-pixel budget is more reliable than a
proportional value because that chrome is a fixed height regardless of screen size. proportional value because that chrome is a fixed height regardless of screen size. It
defaults to `240px` and is configurable (see Settings below).
The `min-height: 0` is essential: Kanboard's drag-and-drop sets an inline `min-height` on
each column equal to its full content height, and in CSS `min-height` beats `max-height`,
so without this reset the tall columns would never shrink.
The override only applies while columns are collapsed (when core adds the The override only applies while columns are collapsed (when core adds the
`board-task-list-compact` class). It changes no data and runs no database migration. `board-task-list-compact` class). It changes no data and runs no database migration.
@@ -53,11 +61,26 @@ 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.
## Tuning ## Top horizontal scrollbar (optional)
Edit the single `240px` value in `Asset/css/shrink-vertically.css`. Increase it if the Kanboard's board scrolls horizontally inside `#board-container`, so its only horizontal
scrollbar is still clipped (for example with a taller theme header); decrease it to make scrollbar is at the very bottom of the board -- on a wide board you must scroll all the
the collapsed columns taller. 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.
## Settings
Go to "Settings -> Shrink Vertically":
- **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
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.
## License ## License

26
Template/config/show.php Normal file
View File

@@ -0,0 +1,26 @@
<div class="page-header">
<h2><?= t('Shrink Vertically') ?></h2>
</div>
<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('plugin' => 'ShrinkVertically')) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<fieldset>
<?= $this->form->label(t('Vertical offset in pixels'), 'shrink_vertically_offset') ?>
<?= $this->form->number('shrink_vertically_offset', $values, $errors) ?>
<p class="form-help">
<?= t('Pixels reserved above and below the vertically collapsed board columns (page header, column header and the horizontal scrollbar). A larger value makes the columns shorter. Default: 240.') ?>
</p>
</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>
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
</div>
</form>

View File

@@ -0,0 +1,3 @@
<li <?= $this->app->checkMenuSelection('ConfigController', 'show', 'ShrinkVertically') ?>>
<?= $this->url->link(t('Shrink Vertically'), 'ConfigController', 'show', array('plugin' => 'ShrinkVertically')) ?>
</li>

View File

@@ -0,0 +1,3 @@
<style type="text/css">
:root { --sv-shrink-offset: <?= (int) $this->app->config('shrink_vertically_offset', 240) ?>px; }
</style>

1
VERSION Normal file
View File

@@ -0,0 +1 @@
ShrinkVertically v1.0