Files
OpenPipe-llm/src/components/nav/AppShell.tsx
2023-07-10 14:24:00 -06:00

130 lines
3.6 KiB
TypeScript

import {
Heading,
VStack,
Icon,
HStack,
Image,
Grid,
GridItem,
Divider,
Text,
Box,
type BoxProps,
type LinkProps,
Link,
} from "@chakra-ui/react";
import Head from "next/head";
import { BsGithub, BsTwitter } from "react-icons/bs";
import { useRouter } from "next/router";
import PublicPlaygroundWarning from "../PublicPlaygroundWarning";
import { type IconType } from "react-icons";
import { RiFlaskLine } from "react-icons/ri";
import { useState, useEffect } from "react";
type IconLinkProps = BoxProps & LinkProps & { label: string; icon: IconType; href: string };
const IconLink = ({ icon, label, href, target, ...props }: IconLinkProps) => {
const isActive = useRouter().pathname.startsWith(href);
return (
<Box
as={Link}
href={href}
target={target}
w="full"
bgColor={isActive ? "gray.300" : "transparent"}
_hover={{ bgColor: "gray.300" }}
py={4}
justifyContent="start"
cursor="pointer"
{...props}
>
<HStack w="full" px={4} color="gray.700">
<Icon as={icon} boxSize={6} mr={2} />
<Text fontWeight="bold">{label}</Text>
</HStack>
</Box>
);
};
const NavSidebar = () => {
return (
<VStack align="stretch" bgColor="gray.100" py={2} pb={0} height="100%">
<Link href="/" w="full" _hover={{ textDecoration: "none" }}>
<HStack spacing={0} pl="4">
<Image src="/logo.svg" alt="" w={6} h={6} />
<Heading size="md" p={2} pl={{ base: 16, md: 2 }}>
OpenPipe
</Heading>
</HStack>
</Link>
<Divider />
<VStack spacing={0} align="flex-start" overflowY="auto" overflowX="hidden" flex={1}>
<IconLink icon={RiFlaskLine} label="Experiments" href="/experiments" />
</VStack>
<Divider />
<VStack w="full" spacing={0} pb={2}>
<IconLink
icon={BsGithub}
label="GitHub"
href="https://github.com/openpipe/openpipe"
target="_blank"
color="gray.500"
_hover={{ color: "gray.800" }}
/>
<IconLink
icon={BsTwitter}
label="Twitter"
href="https://twitter.com/corbtt"
target="_blank"
color="gray.500"
_hover={{ color: "gray.800" }}
/>
</VStack>
</VStack>
);
};
export default function AppShell(props: { children: React.ReactNode; title?: string }) {
const [vh, setVh] = useState("100vh"); // Default height to prevent flicker on initial render
useEffect(() => {
const setHeight = () => {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty("--vh", `${vh}px`);
setVh(`calc(var(--vh, 1vh) * 100)`);
};
setHeight(); // Set the height at the start
window.addEventListener("resize", setHeight);
window.addEventListener("orientationchange", setHeight);
return () => {
window.removeEventListener("resize", setHeight);
window.removeEventListener("orientationchange", setHeight);
};
}, []);
return (
<Grid
h={vh}
w="100vw"
templateColumns={{ base: "56px minmax(0, 1fr)", md: "200px minmax(0, 1fr)" }}
templateRows="max-content 1fr"
templateAreas={'"warning warning"\n"sidebar main"'}
>
<Head>
<title>{props.title ? `${props.title} | OpenPipe` : "OpenPipe"}</title>
</Head>
<GridItem area="warning">
<PublicPlaygroundWarning />
</GridItem>
<GridItem area="sidebar" overflow="hidden">
<NavSidebar />
</GridItem>
<GridItem area="main" overflowY="auto">
{props.children}
</GridItem>
</Grid>
);
}