Files
OrganonTweaks/Asset/js/relocate.js

99 lines
3.3 KiB
JavaScript
Raw Normal View History

2026-07-04 10:09:55 -03:00
/*
* OrganonTweaks -- move plugin-injected DOM bits into their native positions on the board.
2026-07-04 10:09:55 -03:00
*
* Both fixes are needed because Kanboard's relevant hooks render at the END of their
* container, not where the item belongs:
*
* 1. "Remove this Column": the template:board:column:dropdown hook renders AFTER the column
* menu <ul> (and the dropdown JS clones only that <ul>), so we move the hidden <li> into
* the column's real <ul>.
* 2. "Always show comment icon": the template:board:task:icons hook renders at the end of
* the card icons row, so the "+" bubble lands after priority instead of in the native
* comment slot. We move it just before the description icon (where the native comment
* icon sits), so the order matches whether or not the card has comments.
*
* Both re-run when the board is redrawn (AJAX polling / drag-and-drop refresh).
2026-07-04 10:09:55 -03:00
*/
(function () {
"use strict";
function relocateColumnMenuItems() {
2026-07-04 10:09:55 -03:00
var items = document.querySelectorAll(".organontweaks-remove-column-item");
for (var i = 0; i < items.length; i++) {
var li = items[i];
// Already moved into a menu list: just make sure it is visible.
if (li.closest("ul")) {
li.style.display = "";
continue;
}
// The hook renders the item as a child of the column's dropdown wrapper;
// its first <ul> is the native menu.
var menu = li.parentNode ? li.parentNode.querySelector("ul") : null;
if (menu) {
menu.appendChild(li);
li.style.display = "";
}
}
}
function repositionCommentIcons() {
var items = document.querySelectorAll(".organontweaks-comment-add");
for (var i = 0; i < items.length; i++) {
var item = items[i];
var row = item.parentNode;
if (!row) {
continue;
}
// 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.
var anchor = null;
var descIcon = row.querySelector("i.fa-file-text-o");
if (descIcon) {
anchor = descIcon.closest("a");
}
if (!anchor) {
anchor = row.querySelector(".task-icon-age") || row.querySelector(".task-board-closed");
}
// Move only if needed (keeps the MutationObserver from looping).
if (anchor && anchor.parentNode === row && item.nextElementSibling !== anchor) {
row.insertBefore(item, anchor);
}
}
}
function apply() {
relocateColumnMenuItems();
repositionCommentIcons();
}
2026-07-04 10:09:55 -03:00
function init() {
if (!document.getElementById("board")) {
return;
}
apply();
2026-07-04 10:09:55 -03:00
var container = document.getElementById("board-container");
if (container && window.MutationObserver) {
new MutationObserver(apply).observe(container, { childList: true, subtree: true });
2026-07-04 10:09:55 -03:00
}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();