The nav components learned where the user was from the `navigate` CustomEvent, which only the outbound path dispatches: `popstate` calls `handleNavigate()` directly. So a back-navigation left both of them highlighting the view just left — desktop included, at any width, on any back across two primary views. Opening a detail view was the same cause wearing a different symptom: `app-sidebar` guarded on its own item list and kept its highlight, `bottom-nav` did not and lit nothing. It cannot be fixed by re-dispatching `navigate` — `index.ts` is that event's document listener, so that is an infinite loop, and "please go to X" is not the statement being made. `activeViewStore` is the shell saying "the active view is now X", once per navigation, `popstate` included; both navs read it through a controller and hold no `activeView` of their own. A store rather than an event because a component that mounts *after* a navigation still has to know: `bottom-nav`'s drawer builds its `app-sidebar` on open, and that copy had heard nothing at all, so the drawer opened on Home from any page in the app. Closes #72
60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
import type {
|
|
ReactiveController,
|
|
ReactiveControllerHost,
|
|
} from 'lit';
|
|
import { activeViewStore } from '../active-view-store';
|
|
|
|
/**
|
|
* ActiveViewController connects a Lit component to the
|
|
* ActiveViewStore.
|
|
*
|
|
* Usage in a component:
|
|
*
|
|
* private activeCtrl = new ActiveViewController(this);
|
|
*
|
|
* render() {
|
|
* const lit = this.activeCtrl.isActive('albums');
|
|
* }
|
|
*
|
|
* It reads through to the store rather than copying the value into a
|
|
* `@state()` field, which is the point of #72: two components holding
|
|
* their own idea of the active view is what let them disagree with the
|
|
* shell and with each other.
|
|
*/
|
|
export class ActiveViewController implements ReactiveController {
|
|
private host: ReactiveControllerHost;
|
|
private unsubscribe?: () => void;
|
|
|
|
constructor(host: ReactiveControllerHost) {
|
|
this.host = host;
|
|
host.addController(this);
|
|
}
|
|
|
|
// ===============================================================
|
|
// LIFECYCLE HOOKS
|
|
// ===============================================================
|
|
|
|
hostConnected(): void {
|
|
this.unsubscribe = activeViewStore.subscribe(() => {
|
|
this.host.requestUpdate();
|
|
});
|
|
}
|
|
|
|
hostDisconnected(): void {
|
|
this.unsubscribe?.();
|
|
}
|
|
|
|
// ===============================================================
|
|
// DATA ACCESS
|
|
// ===============================================================
|
|
|
|
/** The active primary view, e.g. `albums`. */
|
|
get current(): string {
|
|
return activeViewStore.get();
|
|
}
|
|
|
|
isActive(view: string): boolean {
|
|
return activeViewStore.isActive(view);
|
|
}
|
|
}
|