import { useState, useRef, useEffect } from "react"; import { splitDragState, notifyDragEnd } from "@/lib/splitDragging"; import { renderTabContent, tabIcon } from "@/shell/tabUtils"; import type { Tab, TabType, Host, SplitMode } from "@/types/ui-types"; // ─── Types ──────────────────────────────────────────────────────────────────── type RowColSizes = number[][]; // ─── useSplitSizes ──────────────────────────────────────────────────────────── function defaultSizes(mode: SplitMode): { rowSizes: number[]; rowColSizes: RowColSizes; } { switch (mode) { case "2-way": return { rowSizes: [100], rowColSizes: [[50, 50]] }; case "3-way": return { rowSizes: [50, 50], rowColSizes: [[50, 50], [100]] }; case "4-way": return { rowSizes: [50, 50], rowColSizes: [ [50, 50], [50, 50], ], }; case "5-way": return { rowSizes: [50, 50], rowColSizes: [ [33.3, 33.3, 33.4], [33.3, 66.7], ], }; case "6-way": return { rowSizes: [50, 50], rowColSizes: [ [33.3, 33.3, 33.4], [33.3, 33.3, 33.4], ], }; default: return { rowSizes: [100], rowColSizes: [[100]] }; } } function useSplitSizes(splitMode: SplitMode) { const init = defaultSizes(splitMode); const [rowSizes, setRowSizes] = useState(init.rowSizes); const [rowColSizes, setRowColSizes] = useState(init.rowColSizes); const [isDragging, setIsDragging] = useState(false); const containerRef = useRef(null); useEffect(() => { const d = defaultSizes(splitMode); setRowSizes(d.rowSizes); setRowColSizes(d.rowColSizes); }, [splitMode]); function reset() { const d = defaultSizes(splitMode); setRowSizes(d.rowSizes); setRowColSizes(d.rowColSizes); } function startDrag() { splitDragState.active = true; setIsDragging(true); } function endDrag() { splitDragState.active = false; setIsDragging(false); notifyDragEnd(); } function onRowDivider(e: React.MouseEvent, rowIdx: number) { e.preventDefault(); const container = containerRef.current; if (!container) return; startDrag(); const totalH = container.getBoundingClientRect().height; const startY = e.clientY; const a = rowSizes[rowIdx]; const b = rowSizes[rowIdx + 1]; function onMove(ev: MouseEvent) { const delta = ((ev.clientY - startY) / totalH) * 100; const na = Math.max(10, Math.min(a + b - 10, a + delta)); setRowSizes((prev) => { const n = [...prev]; n[rowIdx] = na; n[rowIdx + 1] = a + b - na; return n; }); } function onUp() { endDrag(); window.removeEventListener("mousemove", onMove); window.removeEventListener("mouseup", onUp); } window.addEventListener("mousemove", onMove); window.addEventListener("mouseup", onUp); } function onRowDividerTouch(e: React.TouchEvent, rowIdx: number) { const container = containerRef.current; if (!container) return; startDrag(); const totalH = container.getBoundingClientRect().height; const startY = e.touches[0].clientY; const a = rowSizes[rowIdx]; const b = rowSizes[rowIdx + 1]; function onMove(ev: TouchEvent) { const delta = ((ev.touches[0].clientY - startY) / totalH) * 100; const na = Math.max(10, Math.min(a + b - 10, a + delta)); setRowSizes((prev) => { const n = [...prev]; n[rowIdx] = na; n[rowIdx + 1] = a + b - na; return n; }); } function onUp() { endDrag(); window.removeEventListener("touchmove", onMove); window.removeEventListener("touchend", onUp); } window.addEventListener("touchmove", onMove, { passive: false }); window.addEventListener("touchend", onUp); } function onColDivider(e: React.MouseEvent, rowIdx: number, colIdx: number) { e.preventDefault(); const container = containerRef.current; if (!container) return; startDrag(); const totalW = container.getBoundingClientRect().width; const startX = e.clientX; const cols = rowColSizes[rowIdx]; const a = cols[colIdx]; const b = cols[colIdx + 1]; function onMove(ev: MouseEvent) { const delta = ((ev.clientX - startX) / totalW) * 100; const na = Math.max(10, Math.min(a + b - 10, a + delta)); setRowColSizes((prev) => { const next = prev.map((r) => [...r]); next[rowIdx][colIdx] = na; next[rowIdx][colIdx + 1] = a + b - na; return next; }); } function onUp() { endDrag(); window.removeEventListener("mousemove", onMove); window.removeEventListener("mouseup", onUp); } window.addEventListener("mousemove", onMove); window.addEventListener("mouseup", onUp); } function onColDividerTouch( e: React.TouchEvent, rowIdx: number, colIdx: number, ) { const container = containerRef.current; if (!container) return; startDrag(); const totalW = container.getBoundingClientRect().width; const startX = e.touches[0].clientX; const cols = rowColSizes[rowIdx]; const a = cols[colIdx]; const b = cols[colIdx + 1]; function onMove(ev: TouchEvent) { const delta = ((ev.touches[0].clientX - startX) / totalW) * 100; const na = Math.max(10, Math.min(a + b - 10, a + delta)); setRowColSizes((prev) => { const next = prev.map((r) => [...r]); next[rowIdx][colIdx] = na; next[rowIdx][colIdx + 1] = a + b - na; return next; }); } function onUp() { endDrag(); window.removeEventListener("touchmove", onMove); window.removeEventListener("touchend", onUp); } window.addEventListener("touchmove", onMove, { passive: false }); window.addEventListener("touchend", onUp); } return { rowSizes, rowColSizes, isDragging, containerRef, reset, onRowDivider, onRowDividerTouch, onColDivider, onColDividerTouch, }; } // ─── Dividers ───────────────────────────────────────────────────────────────── function ColDivider({ onMouseDown, onTouchStart, }: { onMouseDown: (e: React.MouseEvent) => void; onTouchStart: (e: React.TouchEvent) => void; }) { return (
); } function RowDivider({ onMouseDown, onTouchStart, }: { onMouseDown: (e: React.MouseEvent) => void; onTouchStart: (e: React.TouchEvent) => void; }) { return (
); } // ─── Pane ───────────────────────────────────────────────────────────────────── function PaneHeader({ tab, paneIndex, }: { tab: Tab | null; paneIndex: number; }) { return (
{tab ? ( <> {tabIcon(tab.type)} {tab.type === "dashboard" ? "Dashboard" : tab.label} ) : ( Pane {paneIndex + 1} — empty )}
); } function EmptyPane() { return (
No tab assigned
); } function Pane({ tab, paneIndex, isDragging, onOpenSingletonTab, onOpenTab, }: { tab: Tab | null; paneIndex: number; isDragging: boolean; onOpenSingletonTab: (type: TabType) => void; onOpenTab: (host: Host, type: TabType) => void; }) { return (
{tab ? ( renderTabContent(tab, onOpenSingletonTab, onOpenTab) ) : ( )}
{isDragging && (
)}
); } // ─── SplitView ──────────────────────────────────────────────────────────────── export function SplitView({ tabs, paneTabIds, splitMode, onOpenSingletonTab, onOpenTab, }: { tabs: Tab[]; paneTabIds: (string | null)[]; splitMode: SplitMode; onOpenSingletonTab: (type: TabType) => void; onOpenTab: (host: Host, type: TabType) => void; }) { const { rowSizes, rowColSizes, isDragging, containerRef, reset, onRowDivider, onRowDividerTouch, onColDivider, onColDividerTouch, } = useSplitSizes(splitMode); function pane(idx: number) { const tab = paneTabIds[idx] != null ? (tabs.find((t) => t.id === paneTabIds[idx]) ?? null) : null; return ( ); } function Row({ rowIdx, paneIndices, }: { rowIdx: number; paneIndices: number[]; }) { const cols = rowColSizes[rowIdx] ?? []; return (
{paneIndices.map((pIdx, ci) => ( <>
{pane(pIdx)}
{ci < paneIndices.length - 1 && ( onColDivider(e, rowIdx, ci)} onTouchStart={(e) => onColDividerTouch(e, rowIdx, ci)} /> )} ))}
); } return (
{splitMode === "2-way" && } {splitMode === "3-way" && (
{pane(0)}
onColDivider(e, 0, 0)} onTouchStart={(e) => onColDividerTouch(e, 0, 0)} />
{pane(1)}
onRowDivider(e, 0)} onTouchStart={(e) => onRowDividerTouch(e, 0)} />
{pane(2)}
)} {splitMode === "4-way" && (
onRowDivider(e, 0)} onTouchStart={(e) => onRowDividerTouch(e, 0)} />
)} {splitMode === "5-way" && (
onRowDivider(e, 0)} onTouchStart={(e) => onRowDividerTouch(e, 0)} />
)} {splitMode === "6-way" && (
onRowDivider(e, 0)} onTouchStart={(e) => onRowDividerTouch(e, 0)} />
)}
); }