Set wiki overlay scrim tokens to theme charcoal (#26)

Companion to sow-nodebb-plugin-wiki#22: the plugin now exposes `--wiki-scrim`/`--wiki-scrim-strong` for drawer and dialog backdrops. Override them from `--wg-bg-deep` so overlays match the plum/gold palette instead of the plugin's slate-blue defaults. Verified in the dev forum bundle.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #26
Co-authored-by: vickydotbat <vickydotbat@tutamail.com>
Co-committed-by: vickydotbat <vickydotbat@tutamail.com>
This commit was merged in pull request #26.
This commit is contained in:
2026-07-16 18:13:49 +00:00
committed by archvillainette
parent c3851107ab
commit 60bcd589c0
2 changed files with 4 additions and 224 deletions
@@ -1,224 +0,0 @@
/* ============================================================================
SHADOWS OVER WESTGATE — top bar controller
----------------------------------------------------------------------------
Paste into ACP -> Settings -> Custom Content -> "Custom JavaScript".
(In the live theme this can instead live in the theme's client-side bundle.)
WHY THIS FILE EXISTS
- The static page (join-the-team.html) needs NO JavaScript; it is pure
HTML/CSS (the only inline bit is an <img onerror> fallback). Home lives
in the westgate-pages plugin now (templates/home.tpl).
- The TOP BAR needs this small controller for: Forums mega-menu, inline
search, the mobile burger/drawer, and the drawer's Forums accordion.
NODEBB-SPECIFIC NOTES
- NodeBB is a single-page app: it swaps #content on navigation instead of
reloading. So we DON'T bind to specific nodes on DOMContentLoaded — we use
event delegation on `document`, which keeps working across page changes and
even if the header is re-rendered.
- Inline <script> inside an HTML widget is sanitised/unreliable, which is the
other reason the logic lives here rather than in the pasted markup.
- The live notification / chat / user dropdowns come from Harmony and carry
their OWN JavaScript. If you keep those, you do NOT need this script to open
them — only the Forums menu, search, and mobile drawer below.
- The preview-only member/guest switch from top-bar.html is intentionally
omitted; in production the cluster is chosen server-side ({{{ if loggedIn }}}).
========================================================================== */
(function () {
'use strict';
function bar() { return document.querySelector('.wg-topbar'); }
function closeMenus(exceptKey) {
var b = bar();
if (!b) return;
b.querySelectorAll('[data-wg-panel]').forEach(function (p) {
if (p.getAttribute('data-wg-panel') !== exceptKey) p.classList.remove('is-open');
});
b.querySelectorAll('[data-wg-menu]').forEach(function (t) {
if (t.getAttribute('data-wg-menu') !== exceptKey) t.setAttribute('aria-expanded', 'false');
});
}
function closeSearch() {
var b = bar();
if (!b) return;
var input = b.querySelector('[data-wg-search-input]');
if (input && input.value.trim() !== '') return; // keep open if the user typed
b.classList.remove('is-searching');
}
var CHECK_SVG = '<svg class="chk" width="13" height="13" viewBox="0 0 13 13" fill="none"><path d="M2.5 7l3 3 5-6.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>';
function applyPresence(key) {
var b = bar();
if (!b) return;
var row = b.querySelector('[data-presence="' + key + '"]');
if (!row) return;
b.setAttribute('data-presence', key); // switches the --tb-presence avatar ring
b.querySelectorAll('[data-presence]').forEach(function (x) {
x.classList.remove('is-active');
var c = x.querySelector('.chk'); if (c) c.remove();
});
row.classList.add('is-active');
if (!row.querySelector('.chk')) row.insertAdjacentHTML('beforeend', CHECK_SVG);
}
function restorePresence() {
try {
var saved = localStorage.getItem('wg-topbar-presence');
if (saved) applyPresence(saved);
} catch (_) {}
}
// ---- one delegated click handler for the whole bar ----
document.addEventListener('click', function (e) {
var b = bar();
if (!b) return;
// dropdown trigger (Forums / and any future data-wg-menu)
var trigger = e.target.closest && e.target.closest('[data-wg-menu]');
if (trigger && b.contains(trigger)) {
e.stopPropagation();
var key = trigger.getAttribute('data-wg-menu');
var panel = b.querySelector('[data-wg-panel="' + key + '"]');
var open = panel && panel.classList.contains('is-open');
closeMenus(open ? null : key);
if (panel) panel.classList.toggle('is-open', !open);
trigger.setAttribute('aria-expanded', String(!open));
return;
}
// clicks inside an open panel shouldn't close it
if (e.target.closest && e.target.closest('[data-wg-panel]')) {
e.stopPropagation();
return;
}
// search toggle
var searchBtn = e.target.closest && e.target.closest('[data-wg-search]');
if (searchBtn && b.contains(searchBtn)) {
e.stopPropagation();
b.classList.add('is-searching');
closeMenus(null);
var input = b.querySelector('[data-wg-search-input]');
if (input) setTimeout(function () { input.focus(); }, 60);
return;
}
// mobile burger
var burger = e.target.closest && e.target.closest('[data-wg-burger]');
if (burger && b.contains(burger)) {
e.stopPropagation();
var drawerOpen = b.classList.toggle('is-drawer');
burger.setAttribute('aria-expanded', String(drawerOpen));
return;
}
// drawer Forums accordion
var sub = e.target.closest && e.target.closest('[data-wg-drawer-sub]');
if (sub && b.contains(sub)) {
var subOpen = sub.getAttribute('aria-expanded') === 'true';
sub.setAttribute('aria-expanded', String(!subOpen));
return;
}
// presence / status selector (drives the avatar ring). LIVE: also POST the
// new status to NodeBB (e.g. socket.emit('user.setStatus', ...)) so it
// persists server-side; here it persists in localStorage so it sticks.
var presence = e.target.closest && e.target.closest('[data-presence]');
if (presence && b.contains(presence)) {
e.stopPropagation();
var key = presence.getAttribute('data-presence');
applyPresence(key);
try { localStorage.setItem('wg-topbar-presence', key); } catch (_) {}
return;
}
// ----- pop-out actions -----
// NOTE: in the LIVE theme the pop-outs ARE Harmony's partials
// (drafts.tpl / notifications.tpl / chats.tpl), and these actions are
// handled by NodeBB core JS (component="notifications/list", drafts/delete,
// chats/mark-all-read, etc). The handlers below only drive THIS preview's
// static rows — drop them when wiring the real includes.
// notification filter tabs
var tab = e.target.closest && e.target.closest('.wg-pop__tab');
if (tab && b.contains(tab)) {
e.stopPropagation();
tab.parentElement.querySelectorAll('.wg-pop__tab').forEach(function (t) { t.classList.remove('is-active'); });
tab.classList.add('is-active');
var unreadOnly = tab.getAttribute('data-filter') === 'unread';
var nlist = tab.closest('[data-wg-panel]').querySelector('[data-component="notifications/list"]');
nlist.querySelectorAll('.wg-notif').forEach(function (row) {
var hide = unreadOnly && !row.classList.contains('unread');
row.style.display = hide ? 'none' : '';
var sep = row.nextElementSibling;
if (sep && sep.classList.contains('wg-pop__sep')) sep.style.display = hide ? 'none' : '';
});
return;
}
// mark-all-read
var markBtn = e.target.closest && e.target.closest('.wg-pop__btn:not(.wg-pop__btn--primary)');
if (markBtn && b.contains(markBtn)) {
e.stopPropagation();
var mPanel = markBtn.closest('[data-wg-panel]');
mPanel.querySelectorAll('.unread').forEach(function (r) { r.classList.remove('unread'); });
mPanel.querySelectorAll('.wg-notif__dot').forEach(function (d) { d.remove(); });
var mKey = mPanel.getAttribute('data-wg-panel');
var mBadge = b.querySelector('.wg-topbar__badge[data-count="' + mKey + '"]');
if (mBadge) mBadge.style.display = 'none';
var tc = mPanel.querySelector('.wg-pop__tabcount'); if (tc) tc.textContent = '0';
return;
}
// draft delete
var del = e.target.closest && e.target.closest('.wg-draft__del');
if (del && b.contains(del)) {
e.preventDefault(); e.stopPropagation();
var dRow = del.closest('.wg-draft');
var dSep = dRow.nextElementSibling || dRow.previousElementSibling;
if (dSep && dSep.classList && dSep.classList.contains('wg-pop__sep')) dSep.remove();
dRow.remove();
var dList = b.querySelector('[data-component="drafts/list"]');
var dBadge = b.querySelector('.wg-topbar__badge[data-count="drafts"]');
var left = dList.querySelectorAll('.wg-draft').length;
if (dBadge) dBadge.textContent = left;
if (left === 0) {
if (dBadge) dBadge.style.display = 'none';
var empty = dList.querySelector('.wg-pop__empty'); if (empty) empty.hidden = false;
}
return;
}
// outside click: close everything that floats
closeMenus(null);
closeSearch();
});
// ---- Escape closes everything ----
document.addEventListener('keydown', function (e) {
if (e.key !== 'Escape') return;
var b = bar();
if (!b) return;
closeMenus(null);
b.classList.remove('is-searching');
b.classList.remove('is-drawer');
var burger = b.querySelector('[data-wg-burger]');
if (burger) burger.setAttribute('aria-expanded', 'false');
});
// ---- close the mobile drawer after an in-app navigation ----
if (window.$ && typeof $.fn === 'object') {
$(window).on('action:ajaxify.end', function () {
var b = bar();
if (!b) return;
b.classList.remove('is-drawer', 'is-searching');
closeMenus(null);
restorePresence();
});
}
// ---- restore the saved status on first load ----
if (document.readyState !== 'loading') restorePresence();
else document.addEventListener('DOMContentLoaded', restorePresence);
})();