fix: defer tab bar active state until switchTo() resolves
The tab bar was setting its `active` attribute synchronously on click, before TabCache.switchTo() finished fetching and injecting the new pane. This caused a visual desync where the tab highlighted immediately but the content area showed a blank flash or stale content. Now the tab bar dispatches the layer-switch event without changing its own active state. The shell event handler sets active only after switchTo() confirms the pane is ready, eliminating the race condition. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e1688e8456
commit
3c26addeae
|
|
@ -333,12 +333,18 @@ export function renderShell(opts: ShellOptions): string {
|
||||||
} catch(e) { tabCache = null; }
|
} catch(e) { tabCache = null; }
|
||||||
|
|
||||||
// ── Tab events ──
|
// ── Tab events ──
|
||||||
|
// Set active on tab bar ONLY after switchTo() confirms the pane is ready.
|
||||||
|
// This prevents the visual desync where the tab highlights before content loads.
|
||||||
tabBar.addEventListener('layer-switch', (e) => {
|
tabBar.addEventListener('layer-switch', (e) => {
|
||||||
const { moduleId } = e.detail;
|
const { layerId, moduleId } = e.detail;
|
||||||
saveTabs();
|
saveTabs();
|
||||||
if (tabCache) {
|
if (tabCache) {
|
||||||
tabCache.switchTo(moduleId).then(ok => {
|
tabCache.switchTo(moduleId).then(ok => {
|
||||||
if (!ok) window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
|
if (ok) {
|
||||||
|
tabBar.setAttribute('active', layerId);
|
||||||
|
} else {
|
||||||
|
window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
|
window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
|
||||||
|
|
@ -353,7 +359,11 @@ export function renderShell(opts: ShellOptions): string {
|
||||||
saveTabs();
|
saveTabs();
|
||||||
if (tabCache) {
|
if (tabCache) {
|
||||||
tabCache.switchTo(moduleId).then(ok => {
|
tabCache.switchTo(moduleId).then(ok => {
|
||||||
if (!ok) window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
|
if (ok) {
|
||||||
|
tabBar.setAttribute('active', 'layer-' + moduleId);
|
||||||
|
} else {
|
||||||
|
window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
|
window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
|
||||||
|
|
|
||||||
|
|
@ -794,14 +794,14 @@ export class RStackTabBar extends HTMLElement {
|
||||||
// Clean up previous document-level listeners to prevent leak
|
// Clean up previous document-level listeners to prevent leak
|
||||||
if (this.#docCleanup) { this.#docCleanup(); this.#docCleanup = null; }
|
if (this.#docCleanup) { this.#docCleanup(); this.#docCleanup = null; }
|
||||||
|
|
||||||
// Tab clicks
|
// Tab clicks — dispatch event but do NOT set active yet.
|
||||||
|
// The shell's event handler calls switchTo() and sets active only after success.
|
||||||
this.#shadow.querySelectorAll<HTMLElement>(".tab").forEach(tab => {
|
this.#shadow.querySelectorAll<HTMLElement>(".tab").forEach(tab => {
|
||||||
tab.addEventListener("click", (e) => {
|
tab.addEventListener("click", (e) => {
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
if (target.classList.contains("tab-close")) return;
|
if (target.classList.contains("tab-close")) return;
|
||||||
const layerId = tab.dataset.layerId!;
|
const layerId = tab.dataset.layerId!;
|
||||||
const moduleId = tab.dataset.moduleId!;
|
const moduleId = tab.dataset.moduleId!;
|
||||||
this.active = layerId;
|
|
||||||
this.trackRecent(moduleId);
|
this.trackRecent(moduleId);
|
||||||
this.dispatchEvent(new CustomEvent("layer-switch", {
|
this.dispatchEvent(new CustomEvent("layer-switch", {
|
||||||
detail: { layerId, moduleId },
|
detail: { layerId, moduleId },
|
||||||
|
|
@ -929,12 +929,11 @@ export class RStackTabBar extends HTMLElement {
|
||||||
this.#shadow.querySelectorAll<HTMLElement>(".layer-plane").forEach(plane => {
|
this.#shadow.querySelectorAll<HTMLElement>(".layer-plane").forEach(plane => {
|
||||||
const layerId = plane.dataset.layerId!;
|
const layerId = plane.dataset.layerId!;
|
||||||
|
|
||||||
// Click to switch layer
|
// Click to switch layer — do NOT set active here, let shell handler confirm
|
||||||
plane.addEventListener("click", (e) => {
|
plane.addEventListener("click", (e) => {
|
||||||
if (this.#flowDragSource || this.#orbitDragging) return;
|
if (this.#flowDragSource || this.#orbitDragging) return;
|
||||||
const layer = this.#layers.find(l => l.id === layerId);
|
const layer = this.#layers.find(l => l.id === layerId);
|
||||||
if (layer) {
|
if (layer) {
|
||||||
this.active = layerId;
|
|
||||||
this.dispatchEvent(new CustomEvent("layer-switch", {
|
this.dispatchEvent(new CustomEvent("layer-switch", {
|
||||||
detail: { layerId, moduleId: layer.moduleId },
|
detail: { layerId, moduleId: layer.moduleId },
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue