mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 03:09:57 +00:00
Six more findings from CodeRabbit on #928. Three are real bugs. 1. THE CHIP HID DOCKED PANES. `_onOpened` decided "did the pane take my element?" from `ownerDocument !== document`. That is true for a pane in a pop-out window — and false for a pane moved into the DOCK, which lives in this very document. So docking a pane stamped `.fb-pane-detached` (display:none !important) onto the panel the user was looking at, and put the stub next to it instead of at its home. The element cannot answer this question — `isConnected` is true in a pane window, `ownerDocument` is this one in the dock. Both were live bugs. Ask the manager, which knows exactly what it handed to the host: `panes.elementOf(id)`. That holds for every host, and for reconciling after the fact (detail == null), which is what a plugin rebuilding its panel mid-pop-out triggers. 2. `.fb-paned` FORCED `display: block !important`. A panel that is `display:flex` or `grid` would be silently re-laid-out while detached — the exact opposite of "placement only", and precisely the kind of surprise this feature exists to avoid. Removed. Making a hidden panel visible is a separate job, and it now belongs to the manager, which does it without touching the panel's display MODE: clear `hidden`, and clear an inline `display:none` if that is how the panel hides. 3. VISIBILITY IS NOW RESTORED. The hosts used to set `el.hidden = false` and never put it back, so the docs' "core only changes placement" was a lie and a panel's hidden state was quietly lost. The manager stashes both `hidden` and the inline `display` on open and restores them on dock: a panel that was closed when you opened its pane from the tray goes back to being closed; one that was open stays open. Plus: - The launcher rebuilt its whole list on every panes:opened/closed — including the one fired by clicking a button in that list — destroying the button under the user's finger and dropping focus to <body>. It now restores focus to the toggled pane's button. - `_copyStyles` cloned every stylesheet link, including the panes.css that pane.html already loads. Skip sheets the pane document already has. - Docs: the chip may route to the DOCK, not always a window (it goes through detach() → the host router). `header` precedence was documented backwards — an explicit `header` wins. And the visibility contract above is now written down rather than being a surprise. Signed-off-by: topkoa <topkoa@gmail.com>
72 lines
2.8 KiB
JavaScript
72 lines
2.8 KiB
JavaScript
/*
|
|
* fee[dB]ack — pane launcher (the "Panes" rail popover).
|
|
*
|
|
* A chip only works for a pane that already has a dialog to hide. Panes with no
|
|
* dialog — a readout, a plugin's optional extra — need somewhere to be opened
|
|
* from, so every registered pane gets one: a checkbox list in the rail.
|
|
*
|
|
* The rail popover is the right home for this precisely because it IS exclusive
|
|
* and transient. It's a menu, not a workspace; the panes it opens are the
|
|
* workspace, and they persist.
|
|
*
|
|
* Populated from the registry, so a plugin that calls panes.register() appears
|
|
* here with no further work. (The system tray will mirror this list.)
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
|
|
const panes = window.feedBack && window.feedBack.panes;
|
|
const bus = window.feedBack;
|
|
if (!panes || !bus || typeof bus.on !== 'function') return;
|
|
|
|
let listEl = null;
|
|
|
|
function render() {
|
|
if (!listEl || !listEl.isConnected) listEl = document.getElementById('v3-rail-panes-list');
|
|
if (!listEl) return;
|
|
const all = panes.list();
|
|
|
|
// Toggling a pane from this list fires panes:opened/closed, which re-renders
|
|
// the list — destroying the very button the user just pressed and dropping
|
|
// focus to <body>. Remember which one had it and give it back, so keyboard
|
|
// and screen-reader users can toggle several panes without losing their place.
|
|
const focusedId = (listEl.contains(document.activeElement) && document.activeElement.dataset)
|
|
? document.activeElement.dataset.paneId : null;
|
|
|
|
listEl.replaceChildren();
|
|
|
|
if (!all.length) {
|
|
const empty = document.createElement('div');
|
|
empty.className = 'v3-pop-empty';
|
|
empty.textContent = 'No panes available.';
|
|
listEl.appendChild(empty);
|
|
return;
|
|
}
|
|
|
|
all.forEach((p) => {
|
|
const b = document.createElement('button');
|
|
b.type = 'button';
|
|
b.className = 'v3-pop-btn';
|
|
b.dataset.paneId = p.id;
|
|
b.setAttribute('aria-pressed', p.open ? 'true' : 'false');
|
|
b.textContent = (p.open ? '● ' : '○ ') + p.icon + ' ' + p.title;
|
|
b.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
if (panes.isOpen(p.id)) panes.close(p.id); else panes.detach(p.id);
|
|
});
|
|
listEl.appendChild(b);
|
|
if (p.id === focusedId) b.focus();
|
|
});
|
|
}
|
|
|
|
// The registry changes when plugins load and when panes open/close. Render is
|
|
// cheap and rare (never on a playback path), so just re-run it.
|
|
bus.on('panes:registered', render);
|
|
bus.on('panes:unregistered', render);
|
|
bus.on('panes:opened', render);
|
|
bus.on('panes:closed', render);
|
|
|
|
if (document.readyState !== 'complete') document.addEventListener('DOMContentLoaded', render);
|
|
else render();
|
|
})();
|