initiate template layout

This commit is contained in:
mphstar 2024-08-21 17:42:56 +07:00
parent 7f367fa670
commit 2f2e4b2925
14 changed files with 269 additions and 13 deletions

View File

@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml+png" href="/assets/images/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>Kedai Susu Tuli - SIBI</title>
</head>
<body>
<div id="root"></div>

88
package-lock.json generated
View File

@ -8,11 +8,16 @@
"name": "ksuli-sibi",
"version": "0.0.0",
"dependencies": {
"clsx": "^2.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"tailwind-merge": "^2.5.2",
"zustand": "^4.5.5"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
"@types/node": "^22.4.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
@ -1234,17 +1239,26 @@
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
},
"node_modules/@types/node": {
"version": "22.4.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.4.2.tgz",
"integrity": "sha512-nAvM3Ey230/XzxtyDcJ+VjvlzpzoHwLsF7JaDRfoI0ytO0mVheerNmM45CtA0yOILXwXXxOrcUWH3wltX+7PSw==",
"dev": true,
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.12",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
"dev": true
"devOptional": true
},
"node_modules/@types/react": {
"version": "18.3.4",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz",
"integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==",
"dev": true,
"devOptional": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@ -1803,6 +1817,14 @@
"node": ">= 6"
}
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -1879,7 +1901,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true
"devOptional": true
},
"node_modules/culori": {
"version": "3.3.0",
@ -3349,6 +3371,14 @@
"react": "^18.3.1"
}
},
"node_modules/react-icons": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz",
"integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==",
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
@ -3689,6 +3719,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tailwind-merge": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.2.tgz",
"integrity": "sha512-kjEBm+pvD+6eAwzJL2Bi+02/9LFLal1Gs61+QB7HvTfQQ0aXwC5LGT8PEt1gS0CWKktKe6ysPTAy3cBC5MeiIg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/dcastil"
}
},
"node_modules/tailwindcss": {
"version": "3.4.10",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz",
@ -3840,6 +3879,12 @@
}
}
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true
},
"node_modules/update-browserslist-db": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
@ -3879,6 +3924,14 @@
"punycode": "^2.1.0"
}
},
"node_modules/use-sync-external-store": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
"integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -4124,6 +4177,33 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zustand": {
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.5.tgz",
"integrity": "sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==",
"dependencies": {
"use-sync-external-store": "1.2.2"
},
"engines": {
"node": ">=12.7.0"
},
"peerDependencies": {
"@types/react": ">=16.8",
"immer": ">=9.0.6",
"react": ">=16.8"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"immer": {
"optional": true
},
"react": {
"optional": true
}
}
}
}
}

View File

@ -10,11 +10,16 @@
"preview": "vite preview"
},
"dependencies": {
"clsx": "^2.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"tailwind-merge": "^2.5.2",
"zustand": "^4.5.5"
},
"devDependencies": {
"@eslint/js": "^9.9.0",
"@types/node": "^22.4.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -1,6 +1,11 @@
import LayoutPage from "./components/templates/LayoutPage";
const App = () => {
return <div>Hello World</div>;
return (
<LayoutPage>
<p>Hello World</p>
</LayoutPage>
);
};
export default App;

View File

@ -0,0 +1,24 @@
import { cn } from "@/lib/utils";
type NavLinkProps = {
href: string;
name: string;
isActive: boolean;
};
const NavLink = ({ href, name, isActive }: NavLinkProps) => {
return (
<a href={href}>
<li
className={cn(
"btn bg-transparent border-none",
isActive ? "text-primary" : ""
)}
>
{name}
</li>
</a>
);
};
export default NavLink;

View File

@ -0,0 +1,14 @@
const FooterPage = () => {
return (
<footer>
<div className="flex flex-row justify-center py-2 bg-white">
<p className="text-xs text-center">
&copy; 2024 <span className="font-medium">Kedai Susu Tuli</span> &
Developed by <span className="font-medium">Mphstar</span>
</p>
</div>
</footer>
);
};
export default FooterPage;

View File

@ -0,0 +1,51 @@
import useNavbarStore from "@/stores/NavbarStore";
import { cn } from "@lib/utils";
import { IoClose } from "react-icons/io5";
import { RxHamburgerMenu } from "react-icons/rx";
import NavLink from "../molecules/NavLink";
const HeaderPage = () => {
const navStore = useNavbarStore();
return (
<div className="bg-white drop-shadow w-full h-fit sticky top-0">
<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">
<img
className="w-10"
src="/assets/images/logo.png"
alt="Logo Kedai Susu Tuli"
/>
<div className="form-control">
<h1 className="font-semibold">K-SULI</h1>
<p className="text-gray-600">Kedai Susu Tuli</p>
</div>
</div>
<ul
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",
navStore.isOpen ? "translate-y-0" : "-translate-y-full",
"duration-300 ease-in-out md:translate-y-0"
)}
>
<div
onClick={() => navStore.toggle()}
className="btn absolute top-4 right-4 md:hidden bg-transparent border-none"
>
<IoClose />
</div>
<NavLink href="/" name="Home" isActive={true} />
<NavLink href="/" name="Kamus" isActive={false} />
<NavLink href="/" name="Kuis" isActive={false} />
</ul>
<button
onClick={() => navStore.toggle()}
className="btn bg-transparent border-none md:hidden"
>
<RxHamburgerMenu />
</button>
</header>
</div>
);
};
export default HeaderPage;

View File

@ -0,0 +1,17 @@
import React from "react";
import HeaderPage from "../organisms/HeaderPage";
import FooterPage from "../organisms/FooterPage";
const LayoutPage = ({ children }: { children: React.ReactNode }) => {
return (
<div className="flex flex-col min-h-svh bg-ground">
<HeaderPage />
<main className="flex flex-col flex-1 container max-w-[1200px]">
{children}
</main>
<FooterPage />
</div>
);
};
export default LayoutPage;

6
src/lib/utils.ts Normal file
View File

@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

13
src/stores/NavbarStore.ts Normal file
View File

@ -0,0 +1,13 @@
import { create } from "zustand";
type NavbarType = {
isOpen: boolean;
toggle: () => void;
};
const useNavbarStore = create<NavbarType>((set) => ({
isOpen: false,
toggle: () => set((state) => ({ isOpen: !state.isOpen })),
}));
export default useNavbarStore;

View File

@ -2,7 +2,32 @@
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
extend: {
container: {
center: true,
padding: "1rem",
},
colors: {
ground: "#FAF7EE",
},
},
},
daisyui: {
themes: [
{
mytheme: {
primary: "#fbbf24",
secondary: "#00b44a",
accent: "#0099db",
neutral: "#080f0e",
"base-100": "#f3f4f6",
info: "#00abf0",
success: "#00e5ab",
warning: "#fb923c",
error: "#e11d48",
},
},
],
},
plugins: [require("daisyui")],
};

View File

@ -18,7 +18,15 @@
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
"noFallthroughCasesInSwitch": true,
/* Aliases */
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@lib/*": ["src/lib/*"]
}
},
"include": ["src"]
}

View File

@ -1,7 +1,15 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
"@components": path.resolve(__dirname, "./src/components"),
"@lib": path.resolve(__dirname, "./src/lib"),
},
},
plugins: [react()],
})
});