feat: slicing kamus page
|
@ -15,6 +15,8 @@
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-router-dom": "^6.26.1",
|
"react-router-dom": "^6.26.1",
|
||||||
|
"react-virtualized-auto-sizer": "^1.0.24",
|
||||||
|
"react-window": "^1.8.10",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"zustand": "^4.5.5"
|
"zustand": "^4.5.5"
|
||||||
},
|
},
|
||||||
|
@ -298,6 +300,22 @@
|
||||||
"@babel/core": "^7.0.0-0"
|
"@babel/core": "^7.0.0-0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
||||||
|
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
||||||
|
"dependencies": {
|
||||||
|
"regenerator-runtime": "^0.14.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/runtime/node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||||
|
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||||
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.25.0",
|
"version": "7.25.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz",
|
||||||
|
@ -3225,6 +3243,11 @@
|
||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/memoize-one": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
||||||
|
},
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
@ -3808,6 +3831,31 @@
|
||||||
"react-dom": ">=16.8"
|
"react-dom": ">=16.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-virtualized-auto-sizer": {
|
||||||
|
"version": "1.0.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.24.tgz",
|
||||||
|
"integrity": "sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-window": {
|
||||||
|
"version": "1.8.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz",
|
||||||
|
"integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.0.0",
|
||||||
|
"memoize-one": ">=3.1.1 <6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">8.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-cache": {
|
"node_modules/read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-icons": "^5.3.0",
|
"react-icons": "^5.3.0",
|
||||||
"react-router-dom": "^6.26.1",
|
"react-router-dom": "^6.26.1",
|
||||||
|
"react-virtualized-auto-sizer": "^1.0.24",
|
||||||
|
"react-window": "^1.8.10",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"zustand": "^4.5.5"
|
"zustand": "^4.5.5"
|
||||||
},
|
},
|
||||||
|
|
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 8.7 KiB |
After Width: | Height: | Size: 8.7 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 8.7 KiB |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 9.3 KiB |
10
src/App.tsx
|
@ -1,5 +1,6 @@
|
||||||
import { lazy, Suspense } from "react";
|
import { lazy, Suspense } from "react";
|
||||||
import { Route, Routes } from "react-router-dom";
|
import { Route, Routes } from "react-router-dom";
|
||||||
|
import Kamus from "./pages/Kamus";
|
||||||
|
|
||||||
const Home = lazy(() => import("@/pages/Home"));
|
const Home = lazy(() => import("@/pages/Home"));
|
||||||
|
|
||||||
|
@ -15,7 +16,14 @@ const App = () => {
|
||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route path="/kamus" element={<div>Kamus</div>} />
|
<Route
|
||||||
|
path="/kamus"
|
||||||
|
element={
|
||||||
|
<Suspense fallback={<div>Loading...</div>}>
|
||||||
|
<Kamus />
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import NavLink from "../molecules/NavLink";
|
||||||
const HeaderPage = () => {
|
const HeaderPage = () => {
|
||||||
const navStore = useNavbarStore();
|
const navStore = useNavbarStore();
|
||||||
return (
|
return (
|
||||||
<div className="bg-white drop-shadow w-full h-fit sticky top-0">
|
<div className="bg-white drop-shadow w-full h-fit sticky top-0 z-[300]">
|
||||||
<header className="flex flex-row items-center gap-2 justify-between px-4 py-3 container max-w-[1200px]">
|
<header className="flex flex-row items-center gap-2 justify-between px-4 py-3 container max-w-[1200px]">
|
||||||
<div className="flex gap-1 items-center">
|
<div className="flex gap-1 items-center">
|
||||||
<img
|
<img
|
||||||
|
@ -22,7 +22,7 @@ const HeaderPage = () => {
|
||||||
</div>
|
</div>
|
||||||
<ul
|
<ul
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex md:flex-row flex-col items-center justify-center fixed md:static min-h-svh md:min-h-0 w-full md:w-fit bg-white/50 md:bg-transparent md:backdrop-blur-none backdrop-blur-md z-[200] top-0 left-0",
|
"flex md:flex-row flex-col items-center justify-center fixed md:static min-h-svh md:min-h-0 w-full md:w-fit bg-white/50 md:bg-transparent md:backdrop-blur-none backdrop-blur-md z-[999] top-0 left-0",
|
||||||
navStore.isOpen ? "translate-y-0" : "-translate-y-full",
|
navStore.isOpen ? "translate-y-0" : "-translate-y-full",
|
||||||
"duration-300 ease-in-out md:translate-y-0"
|
"duration-300 ease-in-out md:translate-y-0"
|
||||||
)}
|
)}
|
||||||
|
@ -33,9 +33,21 @@ const HeaderPage = () => {
|
||||||
>
|
>
|
||||||
<IoClose />
|
<IoClose />
|
||||||
</div>
|
</div>
|
||||||
<NavLink href="/" name="Home" isActive={true} />
|
<NavLink
|
||||||
<NavLink href="/kamus" name="Kamus" isActive={false} />
|
href="/"
|
||||||
<NavLink href="/" name="Kuis" isActive={false} />
|
name="Home"
|
||||||
|
isActive={navStore.navSelected === "home"}
|
||||||
|
/>
|
||||||
|
<NavLink
|
||||||
|
href="/kamus"
|
||||||
|
name="Kamus"
|
||||||
|
isActive={navStore.navSelected === "kamus"}
|
||||||
|
/>
|
||||||
|
<NavLink
|
||||||
|
href="/"
|
||||||
|
name="Kuis"
|
||||||
|
isActive={navStore.navSelected === "kuis"}
|
||||||
|
/>
|
||||||
</ul>
|
</ul>
|
||||||
<button
|
<button
|
||||||
onClick={() => navStore.toggle()}
|
onClick={() => navStore.toggle()}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { FilesetResolver, HandLandmarker } from "@mediapipe/tasks-vision";
|
||||||
import calcLandmarkList from "@/utils/CalculateLandmark";
|
import calcLandmarkList from "@/utils/CalculateLandmark";
|
||||||
import preProcessLandmark from "@/utils/PreProcessLandmark";
|
import preProcessLandmark from "@/utils/PreProcessLandmark";
|
||||||
import ConvertResult from "@/utils/ConvertResult";
|
import ConvertResult from "@/utils/ConvertResult";
|
||||||
|
import useNavbarStore from "@/stores/NavbarStore";
|
||||||
|
|
||||||
type PredictResult = {
|
type PredictResult = {
|
||||||
abjad: String;
|
abjad: String;
|
||||||
|
@ -130,11 +131,19 @@ const Home = () => {
|
||||||
requestAnimationFrame(detectHands);
|
requestAnimationFrame(detectHands);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const store = useNavbarStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
store.setNavSelected("home");
|
||||||
|
|
||||||
loadModel();
|
loadModel();
|
||||||
startWebcam();
|
startWebcam();
|
||||||
|
|
||||||
setLoadCamera(true);
|
setLoadCamera(true);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// stop camera
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import LayoutPage from "@/components/templates/LayoutPage";
|
||||||
|
import useNavbarStore from "@/stores/NavbarStore";
|
||||||
|
import { abjads } from "@/utils/ConvertResult";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
const Kamus = () => {
|
||||||
|
const store = useNavbarStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
store.setNavSelected("kamus");
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<LayoutPage>
|
||||||
|
<div className="flex flex-col flex-1 py-4">
|
||||||
|
<h1 className="font-semibold">Kamus SIBI</h1>
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 mt-6">
|
||||||
|
{abjads.map((abjad, index) => (
|
||||||
|
<CardKamus
|
||||||
|
key={index}
|
||||||
|
title={`Abjad ${abjad.toLocaleUpperCase()}`}
|
||||||
|
image={`/assets/kamus/${abjad.toLocaleLowerCase()}.jpg`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</LayoutPage>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CardKamus = ({ title, image }: { title: string; image: string }) => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col rounded-md bg-base-100 shadow-lg overflow-hidden">
|
||||||
|
<img className="h-[250px] object-cover" src={image} alt="Kamus SIBI" />
|
||||||
|
<div className="p-3 flex items-center justify-center font-semibold">
|
||||||
|
<p>{title}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Kamus;
|
|
@ -3,11 +3,15 @@ import { create } from "zustand";
|
||||||
type NavbarType = {
|
type NavbarType = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
toggle: () => void;
|
toggle: () => void;
|
||||||
|
navSelected: "home" | "kamus" | "kuis";
|
||||||
|
setNavSelected: (nav: "home" | "kamus" | "kuis") => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useNavbarStore = create<NavbarType>((set) => ({
|
const useNavbarStore = create<NavbarType>((set) => ({
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
toggle: () => set((state) => ({ isOpen: !state.isOpen })),
|
toggle: () => set((state) => ({ isOpen: !state.isOpen })),
|
||||||
|
navSelected: "home",
|
||||||
|
setNavSelected: (nav) => set({ navSelected: nav }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useNavbarStore;
|
export default useNavbarStore;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const abjads = [
|
export const abjads = [
|
||||||
"A",
|
"A",
|
||||||
"B",
|
"B",
|
||||||
"C",
|
"C",
|
||||||
|
|