refactor(yazi): render depth-stack list tabs through YaziPaneRow primitive
Convert Discover, Feed, MyShows, and Settings pages from bespoke 3-column flexbox JSX to render entirely via the shared <YaziPaneRow> parent|current| preview primitive. Each page now supplies parent/current/preview content accessors plus labels; the pane row handles layout, borders, focus styling, and the muted placeholder for the blank parent slot at depth 0. Replace Solid's children() helper in YaziPaneRow with a local normalizeContent that hands the raw accessor to a reactive expression. children() flattens to a stable resolved-nodes array and does not re-resolve on truthy<->truthy root swaps (e.g. depth switching a pane's root between a list fragment and an editor), which would freeze the previous subtree. The insert effect from the reactive expression disposes and remounts on element-identity change instead. Add tests/yazi-pages-depth.test.ts covering the nav-store depth-stack contract the pages depend on (push/pop stack growth, parent-slot data model across root→child→grandchild→pop transitions).
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
* />
|
||||
*/
|
||||
|
||||
import { children as solidChildren, createMemo, Show } from "solid-js";
|
||||
import { createMemo } from "solid-js";
|
||||
import type { JSX } from "solid-js";
|
||||
import type { RGBA } from "@opentui/core";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
@@ -64,6 +64,24 @@ function resolveLabel(v: PaneLabel | undefined): string {
|
||||
return typeof v === "function" ? v() : v;
|
||||
}
|
||||
|
||||
/** Normalize a PaneContent (static JSX or accessor) into a reactive accessor.
|
||||
* We deliberately do NOT use Solid's `children()` helper here: that helper
|
||||
* flattens accessor children into a stable resolved-nodes array and is the
|
||||
* wrong tool for content whose ROOT swaps at runtime (e.g. the current pane
|
||||
* switching between a depth-1 list fragment and a depth-2 editor — both
|
||||
* truthy JSX roots). `children()` would not re-resolve on a truthy<@->truthy
|
||||
* root swap, freezing the previous subtree in place. Instead we hand the
|
||||
* raw accessor to a reactive `{ expr ?? <Placeholder/> }` expression below,
|
||||
* which Solid compiles into a tracked `insert` effect that disposes the old
|
||||
* subtree and mounts the new whenever the accessor returns a different
|
||||
* element identity. */
|
||||
function normalizeContent(
|
||||
v: PaneContent | undefined,
|
||||
): () => JSX.Element | undefined {
|
||||
if (v == null) return () => undefined;
|
||||
return typeof v === "function" ? (v as () => JSX.Element) : () => v;
|
||||
}
|
||||
|
||||
function Placeholder(props: { color: () => RGBA }) {
|
||||
return (
|
||||
<box padding={1}>
|
||||
@@ -102,12 +120,20 @@ function YaziPane(props: {
|
||||
borderColor={borderColor()}
|
||||
backgroundColor={theme.background}
|
||||
>
|
||||
<Show
|
||||
when={props.content()}
|
||||
fallback={<Placeholder color={muted} />}
|
||||
>
|
||||
{props.content()}
|
||||
</Show>
|
||||
{/*
|
||||
* Render the content accessor directly via a reactive expression.
|
||||
* `{ accessor() ?? <Placeholder/> }` compiles to a Solid `insert`
|
||||
* effect that re-runs whenever the accessor's tracked signals
|
||||
* change (e.g. `depth()` swapping the root from a list fragment to
|
||||
* an editor). Solid disposes the previously-rendered subtree and
|
||||
* mounts the new element identity. `null`/`undefined` falls back
|
||||
* to the muted placeholder so the parent pane keeps its 1/7 slot
|
||||
* visibly blank at depth 0. This is the correct tool for root
|
||||
* swapping — unlike Solid's `children()` / `<Show>`-children,
|
||||
* which only react to truthiness flips, not truthy<@->truthy root
|
||||
* identity changes.
|
||||
*/}
|
||||
{props.content() ?? <Placeholder color={muted} />}
|
||||
</scrollbox>
|
||||
</box>
|
||||
);
|
||||
@@ -123,10 +149,11 @@ export function YaziPaneRow(props: YaziPaneRowProps) {
|
||||
return typeof f === "function" ? f() : f ?? true;
|
||||
});
|
||||
|
||||
// Normalize static JSX and accessor children into reactive accessors.
|
||||
const parentContent = solidChildren(() => props.parent);
|
||||
const currentContent = solidChildren(() => props.current);
|
||||
const previewContent = solidChildren(() => props.preview);
|
||||
// Normalize static JSX and accessor children into reactive accessors
|
||||
// (see normalizeContent for why we avoid Solid's `children()` helper).
|
||||
const parentContent = normalizeContent(props.parent);
|
||||
const currentContent = normalizeContent(props.current);
|
||||
const previewContent = normalizeContent(props.preview);
|
||||
|
||||
const parentLabel = createMemo(() => resolveLabel(props.parentLabel));
|
||||
const currentLabel = createMemo(() => resolveLabel(props.currentLabel));
|
||||
|
||||
Reference in New Issue
Block a user