"use client" /** * Generic two-pane layout: scrollable **tree / outline** column + **details** column, * with persisted split sizes (`ResizablePanelGroup` `id`) and shared **split hub chrome** * (`ListPageSplitHubChrome`) so tree views match finder / folder panels across the app. * * Domain hubs pass `tree` and `details` nodes; this module stays entity-agnostic. */ import * as React from "react" import { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "../ui/resizable" import { useIsMobile } from "../../hooks/use-mobile" import { useSidebarReflowZoom } from "../../hooks/use-sidebar-reflow-zoom" import { ListPageSplitHubChrome } from "./list-page-split-hub-chrome" import { LIST_PAGE_SPLIT_MILLER_COLUMN_PANEL_CLASS, LIST_PAGE_SPLIT_MILLER_DETAIL_PANEL_CLASS, LIST_PAGE_SPLIT_RESIZABLE_GROUP_CLASS, LIST_PAGE_SPLIT_RESIZABLE_GROUP_STYLE, LIST_PAGE_SPLIT_RESIZABLE_HANDLE_CLASS, LIST_PAGE_SPLIT_RESIZABLE_PANEL_STYLE, } from "./list-page-split-hub-tokens" export interface ListPageTreePanelShellProps { /** Stable id for `react-resizable-panels` layout persistence (per hub / route). */ resizableGroupId: string /** Left column (tree chrome + body). */ tree: React.ReactNode /** Right column (detail / inspector). */ details: React.ReactNode /** Accessible name for the split surface, e.g. “Curriculum tree and details”. */ ariaLabel: string treePanelId?: string detailsPanelId?: string treeDefaultSize?: string treeMinSize?: string treeMaxSize?: string detailsDefaultSize?: string detailsMinSize?: string gutterClassName?: string maxWidthClassName?: string /** Override default min-height band — see `LIST_PAGE_SPLIT_HUB_FILL_STYLE`. */ surfaceStyle?: React.CSSProperties } export function ListPageTreePanelShell({ resizableGroupId, tree, details, ariaLabel, treePanelId = "tree", detailsPanelId = "details", treeDefaultSize = "40%", treeMinSize = "20%", treeMaxSize = "60%", detailsDefaultSize = "60%", detailsMinSize = "30%", gutterClassName, maxWidthClassName, surfaceStyle, }: ListPageTreePanelShellProps) { /** * WCAG 1.4.10 — two inline panes collapse below a usable width on a phone * or at 200% zoom. Stack instead so both columns stay readable. */ const isMobileViewport = useIsMobile() const isReflowViewport = useSidebarReflowZoom() const stacked = isMobileViewport || isReflowViewport return ( {tree} {details} ) }