Compare commits
6 Commits
1eeaf31e52
...
cd618e4d1d
| Author | SHA1 | Date | |
|---|---|---|---|
| cd618e4d1d | |||
| 0843e4b20d | |||
| f3c983be6f | |||
| a57c59bda5 | |||
| edda3f7443 | |||
| 78117e3421 |
5
bun.lock
5
bun.lock
@@ -10,6 +10,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"postcss": "^8.5.3",
|
||||
"preact": "^10.26.2",
|
||||
"preact-iso": "^2.9.1",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"tailwind-variants": "^1.0.0",
|
||||
"tailwindcss": "^4.0.17",
|
||||
@@ -564,6 +565,10 @@
|
||||
|
||||
"preact": ["preact@10.26.4", "", {}, "sha512-KJhO7LBFTjP71d83trW+Ilnjbo+ySsaAgCfXOXUlmGzJ4ygYPWmysm77yg4emwfmoz3b22yvH5IsVFHbhUaH5w=="],
|
||||
|
||||
"preact-iso": ["preact-iso@2.9.1", "", { "peerDependencies": { "preact": ">=10", "preact-render-to-string": ">=6.4.0" } }, "sha512-65+oY5FHeSC3mjq2Dg6v72HYjsTYTqI9hXl1BK6vtUkxwfCqDJ5aqk33zjOH3aSuCY7duWiulyqMQ53GXpPIIQ=="],
|
||||
|
||||
"preact-render-to-string": ["preact-render-to-string@6.5.13", "", { "peerDependencies": { "preact": ">=10" } }, "sha512-iGPd+hKPMFKsfpR2vL4kJ6ZPcFIoWZEcBf0Dpm3zOpdVvj77aY8RlLiQji5OMrngEyaxGogeakTb54uS2FvA6w=="],
|
||||
|
||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||
|
||||
"prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="],
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Preact + TS</title>
|
||||
<title>Антихвост</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"postcss": "^8.5.3",
|
||||
"preact": "^10.26.2",
|
||||
"preact-iso": "^2.9.1",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"tailwind-variants": "^1.0.0",
|
||||
"tailwindcss": "^4.0.17"
|
||||
|
||||
28
src/app.tsx
28
src/app.tsx
@@ -1,13 +1,25 @@
|
||||
import { useSignal } from "@preact/signals";
|
||||
import { FunctionComponent } from "preact";
|
||||
import { ErrorBoundary, LocationProvider, Route, Router, useLocation } from "preact-iso";
|
||||
import "preact/debug";
|
||||
import classes from "./app.module.scss";
|
||||
import Button from "./components/ui/Button";
|
||||
import LoginPage from "./pages/login";
|
||||
import ProfilePage from "./pages/profile";
|
||||
|
||||
const HomePage: FunctionComponent = () => {
|
||||
const location = useLocation();
|
||||
location.route("/login");
|
||||
return <div>Redirecting to login...</div>;
|
||||
};
|
||||
|
||||
export function App() {
|
||||
const counter = useSignal(0);
|
||||
return (
|
||||
<>
|
||||
<h1 class={classes.text}>Hello, World!</h1>
|
||||
<Button onClick={() => counter.value++}>Count: {counter.value}</Button>
|
||||
</>
|
||||
<LocationProvider>
|
||||
<ErrorBoundary>
|
||||
<Router>
|
||||
<Route path="/" component={HomePage} />
|
||||
<Route path="/login" component={LoginPage} />
|
||||
<Route path="/profile" component={ProfilePage} />
|
||||
</Router>
|
||||
</ErrorBoundary>
|
||||
</LocationProvider>
|
||||
);
|
||||
}
|
||||
|
||||
13
src/components/layout.tsx
Normal file
13
src/components/layout.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { FunctionComponent } from "preact";
|
||||
import Menu from "./menu";
|
||||
|
||||
const Layout: FunctionComponent = (props) => {
|
||||
return (
|
||||
<div class="flex min-w-screen flex-row justify-between">
|
||||
{props.children}
|
||||
<Menu />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
9
src/components/menu.module.scss
Normal file
9
src/components/menu.module.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
@reference "../index.scss";
|
||||
|
||||
.menu_container {
|
||||
@apply flex min-h-8 min-w-screen flex-col items-center gap-4 border-l border-l-gray-500 px-5 pt-5 md:min-h-screen md:min-w-[300px];
|
||||
}
|
||||
|
||||
.menu_item {
|
||||
@apply w-full cursor-pointer rounded-md px-6 py-2 text-center hover:bg-gray-200;
|
||||
}
|
||||
62
src/components/menu.tsx
Normal file
62
src/components/menu.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { FunctionComponent } from "preact";
|
||||
import { useLocation } from "preact-iso";
|
||||
import { tv } from "tailwind-variants";
|
||||
import classes from "./menu.module.scss";
|
||||
|
||||
interface MenuItemProps {
|
||||
title: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
const MenuItem: FunctionComponent<MenuItemProps> = ({ title, link }: MenuItemProps) => {
|
||||
const location = useLocation();
|
||||
const active = location.path === link;
|
||||
const menuItemClasses = tv({
|
||||
base: classes.menu_item,
|
||||
variants: {
|
||||
activity: {
|
||||
active: "bg-gray-200",
|
||||
inactive: "bg-gray-100",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
activity: "inactive",
|
||||
},
|
||||
});
|
||||
return (
|
||||
<div class={menuItemClasses({ activity: active ? "active" : "inactive" })} onClick={() => location.route(link)}>
|
||||
{title}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface MenuItems {
|
||||
title: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
const Menu: FunctionComponent = () => {
|
||||
const menu_items: MenuItems[] = [
|
||||
{
|
||||
title: "Профиль",
|
||||
link: "/profile",
|
||||
},
|
||||
{
|
||||
title: "Задачи",
|
||||
link: "/tasks",
|
||||
},
|
||||
{
|
||||
title: "Календарь",
|
||||
link: "/calendar",
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div class={classes.menu_container}>
|
||||
{menu_items.map(({ title, link }) => (
|
||||
<MenuItem title={title} link={link} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Menu;
|
||||
@@ -1,5 +1,5 @@
|
||||
@reference '../../index.scss';
|
||||
|
||||
.button {
|
||||
@apply rounded-2xl border-2 border-black p-5 text-white;
|
||||
@apply rounded-2xl border-2 py-3 font-semibold text-white transition-colors hover:cursor-pointer;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ const button = tv({
|
||||
base: classes.button,
|
||||
variants: {
|
||||
color: {
|
||||
primary: "bg-blue-400",
|
||||
secondary: "bg-red-400",
|
||||
primary: "bg-blue-400 hover:bg-blue-500",
|
||||
secondary: "bg-red-400 hover:bg-red-500",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
5
src/components/ui/Input.module.scss
Normal file
5
src/components/ui/Input.module.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
@reference "../../index.scss";
|
||||
|
||||
.input_field {
|
||||
@apply rounded-md border border-gray-300 p-2 placeholder:transition focus:outline-0 focus:placeholder:opacity-25;
|
||||
}
|
||||
34
src/components/ui/Input.tsx
Normal file
34
src/components/ui/Input.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { FunctionComponent } from "preact";
|
||||
import { tv } from "tailwind-variants";
|
||||
import classes from "./Input.module.scss";
|
||||
|
||||
const input = tv({
|
||||
base: classes.input_field,
|
||||
variants: {
|
||||
"text-align": {
|
||||
center: "text-center",
|
||||
left: "text-left",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
"text-align": "left",
|
||||
},
|
||||
});
|
||||
|
||||
interface InputProps {
|
||||
isPassword?: boolean;
|
||||
placeholder?: string;
|
||||
textAlign?: "center" | "left";
|
||||
}
|
||||
|
||||
const Input: FunctionComponent<InputProps> = ({ isPassword = false, placeholder = "", textAlign }: InputProps) => {
|
||||
return (
|
||||
<input
|
||||
type={isPassword ? "password" : "text"}
|
||||
class={input({ "text-align": textAlign })}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Input;
|
||||
@@ -1 +1,5 @@
|
||||
@import "tailwindcss";
|
||||
@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap");
|
||||
:root {
|
||||
font-family: "Montserrat", sans-serif;
|
||||
}
|
||||
|
||||
9
src/pages/login.module.scss
Normal file
9
src/pages/login.module.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
@reference "../index.scss";
|
||||
|
||||
.login_container {
|
||||
@apply flex min-h-screen flex-col items-center justify-center;
|
||||
}
|
||||
|
||||
.login_card {
|
||||
@apply flex w-[95%] min-w-[300px] flex-col justify-center gap-2 rounded-md border-gray-400 p-5 shadow-md md:w-[350px];
|
||||
}
|
||||
17
src/pages/login.tsx
Normal file
17
src/pages/login.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import Button from "@/components/ui/Button";
|
||||
import Input from "@/components/ui/Input";
|
||||
import { FunctionComponent } from "preact";
|
||||
import classes from "./login.module.scss";
|
||||
const LoginPage: FunctionComponent = () => {
|
||||
return (
|
||||
<div class={classes.login_container}>
|
||||
<div class={classes.login_card}>
|
||||
<Input placeholder="Login" textAlign="center" />
|
||||
<Input isPassword placeholder="Password" textAlign="center" />
|
||||
<Button onClick={() => {}}>Login</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
1
src/pages/profile.module.scss
Normal file
1
src/pages/profile.module.scss
Normal file
@@ -0,0 +1 @@
|
||||
@reference "../index.scss";
|
||||
12
src/pages/profile.tsx
Normal file
12
src/pages/profile.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import Layout from "@/components/layout";
|
||||
import { FunctionComponent } from "preact";
|
||||
|
||||
const ProfilePage: FunctionComponent = () => {
|
||||
return (
|
||||
<Layout>
|
||||
<p>Profile</p>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfilePage;
|
||||
Reference in New Issue
Block a user