mirror of
https://github.com/StepanovPlaton/torrent_frontend.git
synced 2026-04-03 12:20:48 +04:00
Complete game page and add how_to_download page
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
NEXT_PUBLIC_BASE_URL=http://127.0.0.1:3000/api
|
||||
NEXT_PUBLIC_CONTENT_URL=http://127.0.0.1:8000/content/torrent
|
||||
NEXT_PUBLIC_COVER_FULL_URL=http://127.0.0.1:8000/content/images/cover/full_size
|
||||
NEXT_PUBLIC_COVER_PREVIEW_URL=http://127.0.0.1:8000/content/images/cover/preview
|
||||
@@ -1,48 +1,126 @@
|
||||
import { GameService } from "@/entities/game";
|
||||
import { GameCard } from "@/features/gameCard";
|
||||
import { Section } from "@/widgets/section";
|
||||
import clsx from "clsx";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
export default async function Games({
|
||||
params: { game_id },
|
||||
params: { game_id },
|
||||
}: {
|
||||
params: { game_id: number };
|
||||
params: { game_id: number };
|
||||
}) {
|
||||
const gameCards = await GameService.getGameCards();
|
||||
const game = await GameService.getGame(game_id);
|
||||
return (
|
||||
<>
|
||||
{game && (
|
||||
<div className="p-4 flex flex-col lp:flex-row">
|
||||
{game.cover && (
|
||||
<Image
|
||||
src={game.cover}
|
||||
className="rounded-lg w-[60%] aspect-video object-cover"
|
||||
alt=""
|
||||
width={1280}
|
||||
height={720}
|
||||
/>
|
||||
)}
|
||||
<div className="pt-2 max-w-[40%]">
|
||||
<h1 className="text-4xl">{game.title}</h1>
|
||||
<p className="text-md text-justify text-fg4 pt-2">
|
||||
{game.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
const gameCards = await GameService.getGameCards();
|
||||
const game = await GameService.getGame(game_id);
|
||||
return (
|
||||
<>
|
||||
{game && (
|
||||
<div className="p-4 flex flex-col lp:block">
|
||||
{game.cover && (
|
||||
<div className="lp:w-[60%] lp:px-4 lp:pl-0 pt-2 float-left">
|
||||
<Image
|
||||
src={game.cover}
|
||||
className="rounded-lg aspect-video object-cover"
|
||||
alt=""
|
||||
width={1280}
|
||||
height={720}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<span className="pt-2 lp:max-w-[40%]">
|
||||
<h1 className="text-4xl">{game.title}</h1>
|
||||
{game.description && (
|
||||
<p className="text-md text-justify text-fg4 pt-2">
|
||||
{game.description}
|
||||
</p>
|
||||
)}
|
||||
</span>
|
||||
<div className="flex justify-between pt-6">
|
||||
{[
|
||||
[
|
||||
{ name: "Система", value: game.system },
|
||||
{ name: "Процессор", value: game.processor },
|
||||
{ name: "Оперативная память", value: game.memory },
|
||||
{ name: "Видеокарта", value: game.graphics },
|
||||
{ name: "Место на диске", value: game.storage },
|
||||
],
|
||||
[
|
||||
{
|
||||
name: "Версия игры",
|
||||
value: `${
|
||||
game.version
|
||||
} (обновлена ${game.update_date.toLocaleDateString(
|
||||
"ru-ru"
|
||||
)})`,
|
||||
},
|
||||
{ name: "Язык", value: game.language },
|
||||
{ name: "Разработчик", value: game.developer },
|
||||
{
|
||||
name: "Год выхода",
|
||||
value: game.release_date.toLocaleDateString("en-us", {
|
||||
year: "numeric",
|
||||
}),
|
||||
},
|
||||
{ name: "Объём загрузки", value: game.download_size },
|
||||
],
|
||||
].map((section, i) => (
|
||||
<ul key={i} className="w-[48%] bg-bg1 rounded-lg py-1 px-4">
|
||||
{section.map((req) => (
|
||||
<li
|
||||
key={req.name}
|
||||
className="font-bold text-sm lp:text-md py-1"
|
||||
>
|
||||
{req.name + ": "}
|
||||
<span
|
||||
className={clsx(
|
||||
"font-normal",
|
||||
req.value === undefined && "text-fg4"
|
||||
)}
|
||||
>
|
||||
{req.value ?? "Не известно"}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
))}
|
||||
</div>
|
||||
{game.trailer && (
|
||||
<iframe
|
||||
src={game.trailer.replace("/watch?v=", "/embed/")}
|
||||
className="w-full aspect-video rounded-lg my-4"
|
||||
allowFullScreen
|
||||
/>
|
||||
)}
|
||||
<div className="relative w-full flex items-center justify-around">
|
||||
<Link
|
||||
href={
|
||||
process.env.NEXT_PUBLIC_CONTENT_URL + "/" + game.torrent_file
|
||||
}
|
||||
className="p-4 bg-ac0 text-fg1 text-xl rounded-lg"
|
||||
>
|
||||
Скачать {game.title}.torrent
|
||||
</Link>
|
||||
</div>
|
||||
<div className="w-full flex justify-end">
|
||||
<Link className="text-right text-sm" href="/how_to_download">
|
||||
Как скачать игру
|
||||
<br /> с помощью .torrent файла?
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{gameCards && (
|
||||
<Section
|
||||
name="Другие популярные игры"
|
||||
link="/games"
|
||||
invite_text={'Перейти в раздел "Игры"'}
|
||||
>
|
||||
{gameCards.map((card) => (
|
||||
<GameCard key={card.id} card={card} />
|
||||
))}
|
||||
</Section>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
{gameCards && (
|
||||
<Section
|
||||
name="Другие популярные игры"
|
||||
link="/games"
|
||||
invite_text={'Перейти в раздел "Игры"'}
|
||||
>
|
||||
{gameCards.map((card) => (
|
||||
<GameCard key={card.id} card={card} />
|
||||
))}
|
||||
</Section>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,22 +4,22 @@ import { Section } from "@/widgets/section";
|
||||
import { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: ".Torrent: Игры",
|
||||
description:
|
||||
".Torrent: Игры - каталог .torrent файлов для обмена видеоиграми",
|
||||
title: ".Torrent: Игры",
|
||||
description:
|
||||
".Torrent: Игры - каталог .torrent файлов для обмена видеоиграми",
|
||||
};
|
||||
|
||||
export default async function Games() {
|
||||
const gameCards = await GameService.getGameCards();
|
||||
return (
|
||||
<>
|
||||
{gameCards && (
|
||||
<Section>
|
||||
{gameCards.map((card) => (
|
||||
<GameCard key={card.id} card={card} />
|
||||
))}
|
||||
</Section>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
const gameCards = await GameService.getGameCards();
|
||||
return (
|
||||
<>
|
||||
{gameCards && gameCards.length > 0 && (
|
||||
<Section>
|
||||
{gameCards.map((card) => (
|
||||
<GameCard key={card.id} card={card} />
|
||||
))}
|
||||
</Section>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,8 +11,12 @@
|
||||
--color-fg1: #3c3836;
|
||||
--color-fg4: #7c6f64;
|
||||
|
||||
--color-ac0: #83a598;
|
||||
--color-ac1: #fabd2f;
|
||||
--color-ac2: #8ec07c;
|
||||
|
||||
--app-width: 70%;
|
||||
font-size: calc((100vh / 1080) * 24);
|
||||
font-size: calc((100vw / 1920) * 24);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
@@ -23,6 +27,10 @@
|
||||
--color-fg0: #fbf1c7;
|
||||
--color-fg1: #ebdbb2;
|
||||
--color-fg4: #a89984;
|
||||
|
||||
--color-ac0: #076678;
|
||||
--color-ac1: #b57614;
|
||||
--color-ac2: #427b58;
|
||||
}
|
||||
|
||||
html,
|
||||
@@ -37,7 +45,7 @@ body {
|
||||
transition-property: color, background-color, border-color;
|
||||
transition-duration: 0.3s;
|
||||
|
||||
overflow: hidden;
|
||||
/* overflow: hidden; */
|
||||
color: var(--color-fg1);
|
||||
background-color: var(--color-bg0);
|
||||
}
|
||||
@@ -48,8 +56,13 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
@media (max-width: 1024px) {
|
||||
:root {
|
||||
font-size: calc((100vh / 1080) * 16);
|
||||
font-size: calc((100vw / 1920) * 56);
|
||||
}
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
:root {
|
||||
font-size: calc((100vw / 1920) * 64);
|
||||
}
|
||||
}
|
||||
61
src/app/how_to_download/page.tsx
Normal file
61
src/app/how_to_download/page.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: ".Torrent: Как скачать?",
|
||||
description:
|
||||
".Torrent: Как скачать? - краткое руководство по скачиваю данных с помощью .torrent файлов",
|
||||
};
|
||||
|
||||
export default async function HowToDownload() {
|
||||
return (
|
||||
<div className="w-full flex flex-col lp:flex-row justify-between p-4">
|
||||
<div className="w-full p-4 lp:w-[50%] lp:pr-10">
|
||||
<h1 className="text-4xl lp:text-3xl">Как скачать?</h1>
|
||||
<div className="text-fg4 text-justify pt-2">
|
||||
Чтобы скачать что-либо с помощью торрент-файла, выполните следующие
|
||||
шаги:
|
||||
<ul className="*:text-fg4">
|
||||
<li>
|
||||
1. Найдите и загрузите торрент-файл или magnet-ссылку, содержащую
|
||||
информацию о файле, который вы хотите скачать.
|
||||
</li>
|
||||
<li>
|
||||
2. Откройте программу-клиент для загрузки торрентов, например,
|
||||
uTorrent, BitTorrent или qBittorrent.
|
||||
</li>
|
||||
<li>
|
||||
3. В программе-клиенте выберите опцию "Open Torrent File" или "Add
|
||||
Torrent" и выберите торрент-файл, который вы скачали в первом
|
||||
шаге.
|
||||
</li>
|
||||
<li>
|
||||
4. После этого начнется загрузка файлов, указанных в
|
||||
торрент-файле. Вы также можете выбрать папку, куда сохранять
|
||||
скачанные файлы.
|
||||
</li>
|
||||
<li>
|
||||
5. Дождитесь завершения загрузки файлов. После этого вы сможете
|
||||
открыть и использовать скачанные файлы на своем компьютере.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full p-4 lp:w-[50%] lp:pl-10">
|
||||
<h2 className="text-4xl lp:text-3xl">Что такое .torrent файл?</h2>
|
||||
<p className="text-fg4 text-justify pt-2">
|
||||
Торрент-файл (или .torrent-файл) - это небольшой файл, который
|
||||
содержит метаданные о файле или наборе файлов, которые можно загрузить
|
||||
с помощью протокола BitTorrent. В торрент-файле обычно указан адрес
|
||||
трекера (специального сервера, отслеживающего пиров) и хеш-суммы
|
||||
частей файлов, которые необходимы для скачивания.
|
||||
<br />
|
||||
<br /> Пользователь, желающий загрузить файл через BitTorrent, сначала
|
||||
скачивает торрент-файл или magnet-ссылку, загружает ее в
|
||||
торрент-клиент (программу для скачивания торрентов), и затем начинает
|
||||
загрузку файлов, участвуя в обмене данными с другими пользователями
|
||||
(пирами) через сеть BitTorrent.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,27 +7,27 @@ import { Header } from "@/widgets/header";
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: ".Torrent",
|
||||
description:
|
||||
".Torrent - сервис обмена .torrent файлами видеоигр, фильмов и аудиокниг",
|
||||
title: ".Torrent",
|
||||
description:
|
||||
".Torrent - сервис обмена .torrent файлами видеоигр, фильмов и аудиокниг",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
// suppressHydrationWarning for theme support
|
||||
<html lang="ru" suppressHydrationWarning>
|
||||
<body className={inter.className}>
|
||||
<ThemeProvider enableSystem={false} defaultTheme="light">
|
||||
<Header />
|
||||
<div className="w-full h-full max-w-[var(--app-width)] m-auto overflow-y-auto">
|
||||
{children}
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
return (
|
||||
// suppressHydrationWarning for theme support
|
||||
<html lang="ru" suppressHydrationWarning>
|
||||
<body className={inter.className}>
|
||||
<ThemeProvider enableSystem={false} defaultTheme="light">
|
||||
<Header />
|
||||
<div className="w-full h-full max-w-[var(--app-width)] m-auto">
|
||||
{children}
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,20 +3,20 @@ import { GameCard } from "@/features/gameCard";
|
||||
import { Section } from "@/widgets/section";
|
||||
|
||||
export default async function Home() {
|
||||
const gameCards = await GameService.getGameCards();
|
||||
return (
|
||||
<>
|
||||
{gameCards && (
|
||||
<Section
|
||||
name="Игры"
|
||||
link="/games"
|
||||
invite_text={'Перейти в раздел "Игры"'}
|
||||
>
|
||||
{gameCards.map((card) => (
|
||||
<GameCard key={card.id} card={card} />
|
||||
))}
|
||||
</Section>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
const gameCards = await GameService.getGameCards();
|
||||
return (
|
||||
<>
|
||||
{gameCards && gameCards.length > 0 && (
|
||||
<Section
|
||||
name="Игры"
|
||||
link="/games"
|
||||
invite_text={'Перейти в раздел "Игры"'}
|
||||
>
|
||||
{gameCards.map((card) => (
|
||||
<GameCard key={card.id} card={card} />
|
||||
))}
|
||||
</Section>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,33 +2,45 @@ import { z } from "zod";
|
||||
import { gameCardSchema } from "./gameCard";
|
||||
|
||||
export const gameSchema = gameCardSchema.and(
|
||||
z.object({
|
||||
torrent_file: z.string().min(1),
|
||||
language: z.string().optional(),
|
||||
version: z.string().optional(),
|
||||
download_size: z.string().optional(),
|
||||
system: z.string().optional(),
|
||||
processor: z.string().optional(),
|
||||
memory: z.string().optional(),
|
||||
graphics: z.string().optional(),
|
||||
storage: z.string().optional(),
|
||||
upload_date: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((d) => new Date(d)),
|
||||
})
|
||||
z.object({
|
||||
torrent_file: z.string().min(1),
|
||||
trailer: z.string().optional(),
|
||||
|
||||
system: z.string().optional(),
|
||||
processor: z.string().optional(),
|
||||
memory: z.string().optional(),
|
||||
graphics: z.string().optional(),
|
||||
storage: z.string().optional(),
|
||||
|
||||
developer: z.string().optional(),
|
||||
language: z.string().optional(),
|
||||
download_size: z.string().optional(),
|
||||
|
||||
update_date: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((d) => new Date(d)),
|
||||
upload_date: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((d) => new Date(d)),
|
||||
release_date: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((d) => new Date(d)),
|
||||
})
|
||||
);
|
||||
export type GameType = z.infer<typeof gameSchema>;
|
||||
|
||||
export const isGame = (a: any): a is GameType => {
|
||||
return gameSchema.safeParse(a).success;
|
||||
return gameSchema.safeParse(a).success;
|
||||
};
|
||||
|
||||
export const gamesSchema = z.array(z.any()).transform((a) => {
|
||||
const games: GameType[] = [];
|
||||
a.forEach((e) => {
|
||||
if (isGame(e)) games.push(gameSchema.parse(e));
|
||||
else console.error("Game parse error - ", e);
|
||||
});
|
||||
return games;
|
||||
const games: GameType[] = [];
|
||||
a.forEach((e) => {
|
||||
if (isGame(e)) games.push(gameSchema.parse(e));
|
||||
else console.error("Game parse error - ", e);
|
||||
});
|
||||
return games;
|
||||
});
|
||||
|
||||
@@ -1,38 +1,35 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const gameCardSchema = z
|
||||
.object({
|
||||
id: z.number(),
|
||||
title: z.string().min(3),
|
||||
cover: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
release_date: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((d) => new Date(d)),
|
||||
})
|
||||
.transform((card) => {
|
||||
return {
|
||||
...card,
|
||||
cover: card.cover
|
||||
? process.env.NEXT_PUBLIC_COVER_FULL_URL + "/" + card.cover
|
||||
: undefined,
|
||||
cover_preview: card.cover
|
||||
? process.env.NEXT_PUBLIC_COVER_PREVIEW_URL + "/" + card.cover
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
.object({
|
||||
id: z.number(),
|
||||
title: z.string().min(3),
|
||||
cover: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
version: z.string().optional(),
|
||||
})
|
||||
.transform((card) => {
|
||||
return {
|
||||
...card,
|
||||
cover: card.cover
|
||||
? process.env.NEXT_PUBLIC_COVER_FULL_URL + "/" + card.cover
|
||||
: undefined,
|
||||
cover_preview: card.cover
|
||||
? process.env.NEXT_PUBLIC_COVER_PREVIEW_URL + "/" + card.cover
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
export type GameCardType = z.infer<typeof gameCardSchema>;
|
||||
|
||||
export const isGameCard = (a: any): a is GameCardType => {
|
||||
return gameCardSchema.safeParse(a).success;
|
||||
return gameCardSchema.safeParse(a).success;
|
||||
};
|
||||
|
||||
export const gameCardsSchema = z.array(z.any()).transform((a) => {
|
||||
const cards: GameCardType[] = [];
|
||||
a.forEach((e) => {
|
||||
if (isGameCard(e)) cards.push(gameCardSchema.parse(e));
|
||||
else console.error("GameCard parse error - ", e);
|
||||
});
|
||||
return cards;
|
||||
const cards: GameCardType[] = [];
|
||||
a.forEach((e) => {
|
||||
if (isGameCard(e)) cards.push(gameCardSchema.parse(e));
|
||||
else console.error("GameCard parse error - ", e);
|
||||
});
|
||||
return cards;
|
||||
});
|
||||
|
||||
@@ -3,30 +3,30 @@ import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
export const GameCard = ({ card }: { card: GameCardType }) => {
|
||||
return (
|
||||
<Link className="group/gamecard cursor-pointer" href={"/games/" + card.id}>
|
||||
{!!card.cover_preview && (
|
||||
<Image
|
||||
src={card.cover_preview}
|
||||
className="rounded-lg"
|
||||
alt=""
|
||||
width={700}
|
||||
height={400}
|
||||
/>
|
||||
)}
|
||||
<div className="flex items-center justify-between pr-2">
|
||||
<h2 className="text-2xl py-1 group-hover/gamecard:underline underline-offset-4">
|
||||
{card.title}
|
||||
</h2>
|
||||
<span className="text-sm text-fg4">
|
||||
{card.release_date.toLocaleDateString("ru-ru", {
|
||||
year: "numeric",
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-lg tb:text-sm pr-2 text-justify line-clamp-5 text-fg4">
|
||||
{card.description}
|
||||
</p>
|
||||
</Link>
|
||||
);
|
||||
return (
|
||||
<Link className="group/gamecard cursor-pointer" href={"/games/" + card.id}>
|
||||
{!!card.cover_preview && (
|
||||
<Image
|
||||
src={card.cover_preview}
|
||||
className="rounded-lg"
|
||||
alt=""
|
||||
width={700}
|
||||
height={400}
|
||||
/>
|
||||
)}
|
||||
<div className="flex items-center justify-between pr-2">
|
||||
<h2 className="text-2xl py-1 group-hover/gamecard:underline underline-offset-4">
|
||||
{card.title}
|
||||
</h2>
|
||||
{card.version && (
|
||||
<span className="text-xs max-w-[30%] line-clamp-2 text-fg4">
|
||||
{card.version}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-lg tb:text-sm pr-2 text-justify line-clamp-5 text-fg4">
|
||||
{card.description}
|
||||
</p>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import Image from "next/image";
|
||||
|
||||
export const Cover = ({
|
||||
cover,
|
||||
type = "preview",
|
||||
}: {
|
||||
cover: string;
|
||||
type?: "cover" | "preview";
|
||||
}) => {
|
||||
return (
|
||||
<Image
|
||||
src={cover}
|
||||
className="rounded-lg aspect-video object-cover"
|
||||
alt=""
|
||||
width={type === "preview" ? 700 : 1280}
|
||||
height={type === "preview" ? 400 : 720}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
import { Cover } from "./cover";
|
||||
|
||||
export { Cover };
|
||||
@@ -4,59 +4,59 @@ import { MobileMenu } from "./mobileMenu/mobileMenu";
|
||||
import Link from "next/link";
|
||||
|
||||
const sections = [
|
||||
{ title: "Игры", href: "/games" },
|
||||
{ title: "Фильмы", href: "/films" },
|
||||
{ title: "Аудиокниги", href: "/audiobooks" },
|
||||
{ title: "Игры", href: "/games" },
|
||||
{ title: "Фильмы", href: "/films" },
|
||||
{ title: "Аудиокниги", href: "/audiobooks" },
|
||||
];
|
||||
|
||||
export const Header = () => {
|
||||
return (
|
||||
<header className="w-full h-20 bg-bg1">
|
||||
<div
|
||||
className="w-full h-full max-w-[var(--app-width)] m-auto px-5
|
||||
return (
|
||||
<header className="w-full h-20 bg-bg1 sticky top-0">
|
||||
<div
|
||||
className="w-full h-full max-w-[var(--app-width)] m-auto px-5
|
||||
flex items-center justify-between"
|
||||
>
|
||||
<h1 className="text-4xl font-bold flex items-center">
|
||||
<div className="lp:hidden">
|
||||
<MobileMenu sections={sections} />
|
||||
</div>
|
||||
<Link href="/">.Torrent</Link>
|
||||
</h1>
|
||||
<div className="hidden text-2xl dsk:block">
|
||||
{sections.map((section) => (
|
||||
<Link
|
||||
key={section.title}
|
||||
className="px-5 cursor-pointer hover:underline"
|
||||
href={section.href}
|
||||
>
|
||||
{section.title}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col items-end">
|
||||
<span className="flex items-center mb-1 ">
|
||||
<SchemeSwitch />
|
||||
<span className="cursor-pointer flex items-center">
|
||||
<PersonIcon className="mr-1 h-4 w-4" />
|
||||
Войти
|
||||
</span>
|
||||
</span>
|
||||
<label className="flex flex-col items-start relative w-36">
|
||||
<input
|
||||
className="peer/search w-full rounded-lg bg-bg4 px-2"
|
||||
placeholder=" "
|
||||
/>
|
||||
<span
|
||||
className="peer-focus/search:opacity-0
|
||||
>
|
||||
<h1 className="text-4xl font-bold flex items-center">
|
||||
<div className="lp:hidden">
|
||||
<MobileMenu sections={sections} />
|
||||
</div>
|
||||
<Link href="/">.Torrent</Link>
|
||||
</h1>
|
||||
<div className="hidden text-2xl dsk:block">
|
||||
{sections.map((section) => (
|
||||
<Link
|
||||
key={section.title}
|
||||
className="px-5 cursor-pointer hover:underline"
|
||||
href={section.href}
|
||||
>
|
||||
{section.title}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col items-end">
|
||||
<span className="flex items-center mb-1 ">
|
||||
<SchemeSwitch />
|
||||
<span className="cursor-pointer flex items-center">
|
||||
<PersonIcon className="mr-1 h-4 w-4" />
|
||||
Войти
|
||||
</span>
|
||||
</span>
|
||||
<label className="flex flex-col items-start relative w-36">
|
||||
<input
|
||||
className="peer/search w-full rounded-lg bg-bg4 px-2"
|
||||
placeholder=" "
|
||||
/>
|
||||
<span
|
||||
className="peer-focus/search:opacity-0
|
||||
peer-[:not(:placeholder-shown)]/search:opacity-0
|
||||
transition-opacity h-0 flex items-center relative bottom-3"
|
||||
>
|
||||
<SearchIcon className="w-4 h-4 mx-2" />
|
||||
Поиск
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
>
|
||||
<SearchIcon className="w-4 h-4 mx-2" />
|
||||
Поиск
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,34 +1,37 @@
|
||||
import type { Config } from "tailwindcss";
|
||||
|
||||
const config: Config = {
|
||||
content: [
|
||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
backgroundImage: {
|
||||
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
|
||||
"gradient-conic":
|
||||
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
|
||||
},
|
||||
colors: {
|
||||
bg0: "var(--color-bg0)",
|
||||
bg1: "var(--color-bg1)",
|
||||
bg4: "var(--color-bg4)",
|
||||
fg0: "var(--color-fg0)",
|
||||
fg1: "var(--color-fg1)",
|
||||
fg4: "var(--color-fg4)",
|
||||
},
|
||||
},
|
||||
screens: {
|
||||
tb: "640px",
|
||||
lp: "1024px",
|
||||
dsk: "1280px",
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
content: [
|
||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
backgroundImage: {
|
||||
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
|
||||
"gradient-conic":
|
||||
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
|
||||
},
|
||||
colors: {
|
||||
bg0: "var(--color-bg0)",
|
||||
bg1: "var(--color-bg1)",
|
||||
bg4: "var(--color-bg4)",
|
||||
fg0: "var(--color-fg0)",
|
||||
fg1: "var(--color-fg1)",
|
||||
fg4: "var(--color-fg4)",
|
||||
ac0: "var(--color-ac0)",
|
||||
ac1: "var(--color-ac1)",
|
||||
ac2: "var(--color-ac2)",
|
||||
},
|
||||
},
|
||||
screens: {
|
||||
tb: "640px",
|
||||
lp: "1024px",
|
||||
dsk: "1280px",
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
export default config;
|
||||
|
||||
Reference in New Issue
Block a user