flatten
This commit is contained in:
@@ -1,124 +0,0 @@
|
||||
import type { JSX } from "solid-js";
|
||||
import type { RGBA } from "@opentui/core";
|
||||
import { Show, For } from "solid-js";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
|
||||
type PanelConfig = {
|
||||
/** Panel content */
|
||||
content: JSX.Element;
|
||||
/** Panel title shown in header */
|
||||
title?: string;
|
||||
/** Fixed width (leave undefined for flex) */
|
||||
width?: number;
|
||||
/** Whether this panel is currently focused */
|
||||
focused?: boolean;
|
||||
};
|
||||
|
||||
type LayoutProps = {
|
||||
/** Top tab bar */
|
||||
header?: JSX.Element;
|
||||
/** Bottom status bar */
|
||||
footer?: JSX.Element;
|
||||
/** Panels to display left-to-right like a file explorer */
|
||||
panels: PanelConfig[];
|
||||
/** Index of the currently active/focused panel */
|
||||
activePanelIndex?: number;
|
||||
};
|
||||
|
||||
export function Layout(props: LayoutProps) {
|
||||
const panelBg = (index: number): RGBA => {
|
||||
const backgrounds = theme.layerBackgrounds;
|
||||
const layers = [
|
||||
backgrounds?.layer0 ?? theme.background,
|
||||
backgrounds?.layer1 ?? theme.backgroundPanel,
|
||||
backgrounds?.layer2 ?? theme.backgroundElement,
|
||||
backgrounds?.layer3 ?? theme.backgroundMenu,
|
||||
];
|
||||
return layers[Math.min(index, layers.length - 1)];
|
||||
};
|
||||
|
||||
const borderColor = (index: number): RGBA | string => {
|
||||
const isActive = index === (props.activePanelIndex ?? 0);
|
||||
return isActive
|
||||
? (theme.accent ?? theme.primary)
|
||||
: (theme.border ?? theme.textMuted);
|
||||
};
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<box
|
||||
flexDirection="column"
|
||||
width="100%"
|
||||
height="100%"
|
||||
backgroundColor={theme.surface}
|
||||
>
|
||||
{/* Header - tab bar */}
|
||||
<Show when={props.header}>
|
||||
<box
|
||||
style={{
|
||||
height: 3,
|
||||
backgroundColor: theme.surface ?? theme.backgroundPanel,
|
||||
}}
|
||||
>
|
||||
<box style={{ paddingLeft: 1, paddingTop: 0, paddingBottom: 0 }}>
|
||||
{props.header}
|
||||
</box>
|
||||
</box>
|
||||
</Show>
|
||||
|
||||
{/* Main content: side-by-side panels */}
|
||||
<box flexDirection="row" style={{ flexGrow: 1 }}>
|
||||
<For each={props.panels}>
|
||||
{(panel, index) => (
|
||||
<box
|
||||
flexDirection="column"
|
||||
border
|
||||
borderColor={theme.border}
|
||||
backgroundColor={panelBg(index())}
|
||||
style={{
|
||||
flexGrow: panel.width ? 0 : 1,
|
||||
width: panel.width,
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
{/* Panel header */}
|
||||
<Show when={panel.title}>
|
||||
<box
|
||||
style={{
|
||||
height: 1,
|
||||
paddingLeft: 1,
|
||||
paddingRight: 1,
|
||||
backgroundColor:
|
||||
index() === (props.activePanelIndex ?? 0)
|
||||
? (theme.accent ?? theme.primary)
|
||||
: (theme.surface ?? theme.backgroundPanel),
|
||||
}}
|
||||
>
|
||||
<text
|
||||
fg={
|
||||
index() === (props.activePanelIndex ?? 0)
|
||||
? "black"
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<strong>{panel.title}</strong>
|
||||
</text>
|
||||
</box>
|
||||
</Show>
|
||||
|
||||
{/* Panel body */}
|
||||
<box
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
padding: 1,
|
||||
}}
|
||||
>
|
||||
{panel.content}
|
||||
</box>
|
||||
</box>
|
||||
)}
|
||||
</For>
|
||||
</box>
|
||||
</box>
|
||||
);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
|
||||
export type TabId =
|
||||
| "feed"
|
||||
| "shows"
|
||||
| "discover"
|
||||
| "search"
|
||||
| "player"
|
||||
| "settings";
|
||||
|
||||
export type TabDefinition = {
|
||||
id: TabId;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export const tabs: TabDefinition[] = [
|
||||
{ id: "feed", label: "Feed" },
|
||||
{ id: "shows", label: "My Shows" },
|
||||
{ id: "discover", label: "Discover" },
|
||||
{ id: "search", label: "Search" },
|
||||
{ id: "player", label: "Player" },
|
||||
{ id: "settings", label: "Settings" },
|
||||
];
|
||||
|
||||
type TabProps = {
|
||||
tab: TabDefinition;
|
||||
active: boolean;
|
||||
onSelect: (tab: TabId) => void;
|
||||
};
|
||||
|
||||
export function Tab(props: TabProps) {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<box
|
||||
border
|
||||
borderColor={theme.border}
|
||||
onMouseDown={() => props.onSelect(props.tab.id)}
|
||||
style={{
|
||||
padding: 1,
|
||||
backgroundColor: props.active ? theme.primary : "transparent",
|
||||
}}
|
||||
>
|
||||
<text style={{ fg: theme.text }}>
|
||||
{props.active ? "[" : " "}
|
||||
{props.tab.label}
|
||||
{props.active ? "]" : " "}
|
||||
</text>
|
||||
</box>
|
||||
);
|
||||
}
|
||||
@@ -1,43 +1,57 @@
|
||||
import { Tab, type TabId } from "./Tab";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { For } from "solid-js";
|
||||
|
||||
type TabNavigationProps = {
|
||||
interface TabNavigationProps {
|
||||
activeTab: TabId;
|
||||
onTabSelect: (tab: TabId) => void;
|
||||
};
|
||||
}
|
||||
|
||||
export const tabs: TabDefinition[] = [
|
||||
{ id: "feed", label: "Feed" },
|
||||
{ id: "shows", label: "My Shows" },
|
||||
{ id: "discover", label: "Discover" },
|
||||
{ id: "search", label: "Search" },
|
||||
{ id: "player", label: "Player" },
|
||||
{ id: "settings", label: "Settings" },
|
||||
];
|
||||
|
||||
export function TabNavigation(props: TabNavigationProps) {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<box style={{ flexDirection: "row", gap: 1 }}>
|
||||
<Tab
|
||||
tab={{ id: "feed", label: "Feed" }}
|
||||
active={props.activeTab === "feed"}
|
||||
onSelect={props.onTabSelect}
|
||||
/>
|
||||
<Tab
|
||||
tab={{ id: "shows", label: "My Shows" }}
|
||||
active={props.activeTab === "shows"}
|
||||
onSelect={props.onTabSelect}
|
||||
/>
|
||||
<Tab
|
||||
tab={{ id: "discover", label: "Discover" }}
|
||||
active={props.activeTab === "discover"}
|
||||
onSelect={props.onTabSelect}
|
||||
/>
|
||||
<Tab
|
||||
tab={{ id: "search", label: "Search" }}
|
||||
active={props.activeTab === "search"}
|
||||
onSelect={props.onTabSelect}
|
||||
/>
|
||||
<Tab
|
||||
tab={{ id: "player", label: "Player" }}
|
||||
active={props.activeTab === "player"}
|
||||
onSelect={props.onTabSelect}
|
||||
/>
|
||||
<Tab
|
||||
tab={{ id: "settings", label: "Settings" }}
|
||||
active={props.activeTab === "settings"}
|
||||
onSelect={props.onTabSelect}
|
||||
/>
|
||||
<For each={tabs}>
|
||||
{(tab) => (
|
||||
<box
|
||||
border
|
||||
borderColor={theme.border}
|
||||
onMouseDown={() => props.onTabSelect(tab.id)}
|
||||
style={{
|
||||
padding: 1,
|
||||
backgroundColor:
|
||||
tab.id == props.activeTab ? theme.primary : "transparent",
|
||||
}}
|
||||
>
|
||||
<text style={{ fg: theme.text }}>
|
||||
{tab.id == props.activeTab ? "[" : " "}
|
||||
{tab.label}
|
||||
{tab.id == props.activeTab ? "]" : " "}
|
||||
</text>
|
||||
</box>
|
||||
)}
|
||||
</For>
|
||||
</box>
|
||||
);
|
||||
}
|
||||
|
||||
export type TabId =
|
||||
| "feed"
|
||||
| "shows"
|
||||
| "discover"
|
||||
| "search"
|
||||
| "player"
|
||||
| "settings";
|
||||
|
||||
export type TabDefinition = {
|
||||
id: TabId;
|
||||
label: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user