Skip to content

Commit

Permalink
✨ feat: initial layout
Browse files Browse the repository at this point in the history
  • Loading branch information
nxhawk committed Oct 27, 2024
1 parent e0e07ba commit 708d2d9
Show file tree
Hide file tree
Showing 13 changed files with 805 additions and 15 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/english-education.png" />
<link rel="icon" type="image/svg+xml" href="./src/assets/images/english-education.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>English Hub</title>
</head>
Expand Down
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@
"precommit": "yarn lint && yarn prettier"
},
"dependencies": {
"@chakra-ui/react": "^2.10.3",
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.35.0",
"framer-motion": "^11.11.10",
"lucide-react": "^0.453.0",
"next-themes": "^0.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.27.0"
Expand All @@ -41,6 +47,7 @@
"prettier": "3.3.3",
"tailwindcss": "^3.4.7",
"typescript": "^5.2.2",
"vite": "^5.3.4"
"vite": "^5.3.4",
"vite-tsconfig-paths": "^5.0.1"
}
}
9 changes: 8 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { RouterProvider } from "react-router-dom";
import router from "./config/router/router";

function App() {
return <div className="text-red-500 text-6xl font-bold">React Boilerplate</div>;
return (
<>
<RouterProvider router={router} />
</>
);
}

export default App;
File renamed without changes
12 changes: 12 additions & 0 deletions src/components/logo/Logo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Heading } from "@chakra-ui/react";
import React from "react";

Check failure on line 2 in src/components/logo/Logo.tsx

View workflow job for this annotation

GitHub Actions / deploy

'React' is declared but its value is never read.

const Logo = () => {
return (
<Heading as="h5" size="md" textTransform="uppercase">
English Hub
</Heading>
);
};

export default Logo;
16 changes: 16 additions & 0 deletions src/config/router/router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Layout from "@/layout/Layout";
import { createBrowserRouter } from "react-router-dom";

const router = createBrowserRouter(
[
{
element: <Layout />,
path: "*",
},
],
{
basename: "/enghub",
},
);

export default router;
37 changes: 37 additions & 0 deletions src/layout/AppBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Logo from "@/components/logo/Logo";
import { Button, HStack, Text, useMediaQuery } from "@chakra-ui/react";
import { Menu } from "lucide-react";

type Props = {
toggleSidebar: () => void;
};

const AppBar = ({ toggleSidebar }: Props) => {
const [isMobile] = useMediaQuery("(max-width: 680px)");
return (
<HStack
className="bg-sky-800 text-white border-b p-3 fixed top-0 left-0 w-full shadow-2xl z-50"
align="center"
justify="space-between"
>
<HStack spacing="10px">
<Button
size="md"
backgroundColor="transparent"
_hover={{ bg: "rgba(0, 0, 0, 0.08)" }}
onClick={toggleSidebar}
hidden={isMobile}
>
<Menu className="text-white" />
</Button>
<Logo />
</HStack>
<HStack spacing="20px">
<Text fontSize="lg">Login</Text>
<Text fontSize="lg">Register</Text>
</HStack>
</HStack>
);
};

export default AppBar;
27 changes: 27 additions & 0 deletions src/layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from "react";
import AppBar from "./AppBar";
import { HStack } from "@chakra-ui/react";
import SideBar from "./SideBar";
import { Outlet } from "react-router-dom";

const Layout = () => {
const [isSidebarOpen, setIsSidebarOpen] = React.useState<boolean>(true);

const toggleSidebar = (): void => {
setIsSidebarOpen(!isSidebarOpen);
};

return (
<>
<AppBar toggleSidebar={toggleSidebar} />
<HStack position="relative" align="flex-start">
<SideBar isOpen={isSidebarOpen} />
<main style={{ flex: 1, transition: "margin-left 0.3s", marginTop: "64px" }} id="content">
<Outlet />
</main>
</HStack>
</>
);
};

export default Layout;
136 changes: 136 additions & 0 deletions src/layout/SideBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { Button, List, ListItem, Text, useMediaQuery, VStack } from "@chakra-ui/react";
import { ClipboardCheck, Home, ScanSearch, SpellCheck, Tag } from "lucide-react";
import React from "react";
import { useLocation, useNavigate } from "react-router-dom";

type Props = {
isOpen: boolean;
};

class MenuOption {
name: string;
icon: React.ReactElement;
onClick: () => void;

constructor({ name, icon, onClick }: { name: string; icon: React.ReactElement; onClick: () => void }) {
this.name = name;
this.icon = icon;
this.onClick = onClick;
}
}

const SideBar = ({ isOpen }: Props) => {
const navigate = useNavigate();
const [isHovered, setIsHovered] = React.useState(false);
const location = useLocation();
const [isMobile] = useMediaQuery("(max-width: 680px)");
const [selectedIndex, setSelectedIndex] = React.useState<number>(0);

React.useEffect(() => {
switch (location.pathname) {
case "/dictionary":
setSelectedIndex(0);
document.title = "Dictionary";
break;
case "/learn-through-images":
setSelectedIndex(1);
document.title = "Learn Through Images";
break;
case "/check-grammar":
setSelectedIndex(2);
document.title = "Check Grammar";
break;
case "/check-spelling":
setSelectedIndex(3);
document.title = "Check Spelling";
break;
case "/learn-flashcard":
setSelectedIndex(4);
document.title = "Flash Card";
break;
case "/about":
setSelectedIndex(5);
document.title = "About";
break;
default:
setSelectedIndex(-1);
break;
}
}, [location]);

const menuOptions: MenuOption[] = [
new MenuOption({
name: "Dictionary",
icon: <Home />,
onClick: () => {
navigate("/dictionary");
},
}),
new MenuOption({
name: "Learn Through Images",
icon: <ScanSearch />,
onClick: () => {
navigate("/learn-through-images");
},
}),
new MenuOption({
name: "Check Grammar",
icon: <ClipboardCheck />,
onClick: () => {
navigate("/check-grammar");
},
}),
new MenuOption({
name: "Check Spelling",
icon: <SpellCheck />,
onClick: () => {
navigate("/check-spelling");
},
}),
new MenuOption({
name: "Flash Card",
icon: <Tag />,
onClick: () => {
navigate("/learn-flashcard");
},
}),
];

return (
<VStack
position="sticky"
width={(isOpen || isHovered) && !isMobile ? "250px" : "60px"}
height="100vh"
borderRight="1px"
align="flex-start"
overflowY="auto"
overscrollBehavior="contain"
borderColor="#d4d4d4"
className="top-0"
transition="width 0.2s, padding 0.3s"
paddingTop="64px"
>
<List onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} width="full" marginY="8px">
{menuOptions.map((menu, idx) => (
<ListItem key={menu.name}>
<Button
width="full"
leftIcon={menu.icon}
borderRadius="0"
justifyContent="flex-start"
gap="10px"
paddingY="6"
_hover={{ bg: idx === selectedIndex ? "rgb(221, 242, 253)" : "rgba(0, 0, 0, 0.04)" }}
bg={idx === selectedIndex ? "rgb(221, 242, 253)" : "transparent"}
onClick={menu.onClick}
>
{(isOpen || isHovered) && !isMobile && <Text fontSize="lg">{menu.name}</Text>}
</Button>
</ListItem>
))}
</List>
</VStack>
);
};

export default SideBar;
5 changes: 4 additions & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { ChakraProvider } from "@chakra-ui/react";
import App from "./App.tsx";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />
<ChakraProvider>
<App />
</ChakraProvider>
</React.StrictMode>,
);
6 changes: 5 additions & 1 deletion tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,

"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"]
}
3 changes: 2 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import tsconfigPaths from "vite-tsconfig-paths";

// https://vitejs.dev/config/
export default defineConfig({
base: "/enghub",
plugins: [react()],
plugins: [react(), tsconfigPaths()],
preview: {
port: 5173,
strictPort: true,
Expand Down
Loading

0 comments on commit 708d2d9

Please sign in to comment.