React19 update broke components using ref

pull/46/head
v4ltages 4 months ago
parent 018c7c53e5
commit deb07df397
No known key found for this signature in database
GPG Key ID: DC7BC38E0DC642B
  1. 2
      next.config.ts
  2. 2
      package.json
  3. 49
      src/app/kodukord/page.tsx
  4. 110
      src/app/reeglid/[slug]/page.tsx
  5. 34
      src/app/reeglid/page.tsx
  6. 2
      src/app/striim/page.tsx
  7. 58
      src/app/turniirid/page.tsx
  8. 30
      src/components/ui/button.tsx
  9. 5
      src/data/rules/cs2.md

@ -9,7 +9,7 @@ const nextConfig: NextConfig = {
{ {
key: "Content-Security-Policy", key: "Content-Security-Policy",
value: value:
"frame-src 'self' https://player.twitch.tv https://embed.twitch.tv; frame-ancestors 'self';", "frame-src 'self' https://tipilan.ee https://player.twitch.tv https://embed.twitch.tv; frame-ancestors 'self' https://tipilan.ee;",
}, },
{ {
key: "X-Frame-Options", key: "X-Frame-Options",

@ -27,7 +27,6 @@
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"drizzle-orm": "^0.44.2", "drizzle-orm": "^0.44.2",
"gray-matter": "^4.0.3",
"lucide-react": "^0.522.0", "lucide-react": "^0.522.0",
"material-symbols": "^0.31.8", "material-symbols": "^0.31.8",
"next": "15.3.0", "next": "15.3.0",
@ -36,7 +35,6 @@
"react-dom": "^19.1.1", "react-dom": "^19.1.1",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1",
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"three": "^0.178.0", "three": "^0.178.0",
"tw-animate-css": "^1.3.4" "tw-animate-css": "^1.3.4"

@ -1,17 +1,11 @@
// app/kodukord/page.tsx (App Router) // app/kodukord/page.tsx (App Router)
import fs from "node:fs"; import ReactMarkdown, { Components } from "react-markdown";
import path from "node:path";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { vipnagorgialla } from "@/components/Vipnagorgialla"; import { vipnagorgialla } from "@/components/Vipnagorgialla";
import SectionDivider from "@/components/SectionDivider"; import SectionDivider from "@/components/SectionDivider";
export const runtime = "nodejs"; // ensure fs is available (not Edge) export default async function Page() {
export const dynamic = "force-static"; // read at build time const file = Bun.file("src/data/kodukord.md");
const content = await file.text();
export default function Page() {
const filePath = path.join(process.cwd(), "src/data", "kodukord.md");
const content = fs.readFileSync(filePath, "utf8");
return ( return (
<div> <div>
@ -25,24 +19,31 @@ export default function Page() {
<div className="prose prose-lg dark:prose-invert max-w-none"> <div className="prose prose-lg dark:prose-invert max-w-none">
<ReactMarkdown <ReactMarkdown
remarkPlugins={[remarkGfm]} components={
components={{ {
h1: ({node, ...props}) => ( h1: (props) => (
<h1 className="text-3xl md:text-4xl font-bold my-4" {...props} /> <h1 className="text-3xl md:text-4xl font-bold my-4">
), {props.children}
h2: ({node, ...props}) => ( </h1>
<h2 className="text-2xl md:text-3xl font-semibold my-3" {...props} />
), ),
ol: ({node, ...props}) => ( h2: (props) => (
<ol className="list-decimal ml-6 md:text-xl" {...props} /> <h2 className="text-2xl md:text-3xl font-semibold my-3">
{props.children}
</h2>
), ),
ul: ({node, ...props}) => ( ol: (props) => (
<ul className="list-disc ml-6 md:text-xl" {...props} /> <ol className="list-decimal ml-6 md:text-xl">
{props.children}
</ol>
), ),
p: ({node, ...props}) => ( ul: (props) => (
<p className="md:text-xl" {...props} /> <ul className="list-disc ml-6 md:text-xl">
{props.children}
</ul>
), ),
}} p: (props) => <p className="md:text-xl">{props.children}</p>,
} as Components
}
> >
{content} {content}
</ReactMarkdown> </ReactMarkdown>

@ -1,49 +1,103 @@
import { notFound } from "next/navigation";
import ReactMarkdown, { Components } from "react-markdown";
import { vipnagorgialla } from "@/components/Vipnagorgialla"; import { vipnagorgialla } from "@/components/Vipnagorgialla";
import path from "node:path";
import fs from "node:fs/promises";
import ReactMarkdown from "react-markdown";
import SectionDivider from "@/components/SectionDivider"; import SectionDivider from "@/components/SectionDivider";
type Props = { // Map of valid slugs to their corresponding file paths and titles
const rulesMap = {
lol: {
filePath: "src/data/rules/lol.md",
title: "LOL Reeglid",
},
cs2: {
filePath: "src/data/rules/cs2.md",
title: "CS2 Reeglid",
},
} as const;
type RuleSlug = keyof typeof rulesMap;
interface PageProps {
params: Promise<{ slug: string }>; params: Promise<{ slug: string }>;
}; }
export default async function RulePage({params}: Props) { async function getRuleContent(slug: string) {
const {slug} = await params; if (!Object.keys(rulesMap).includes(slug)) {
return null;
}
const filePath = path.join(process.cwd(), "src/data/rules", `${slug}.md`); const ruleConfig = rulesMap[slug as RuleSlug];
let file: string;
try { try {
file = await fs.readFile(filePath, "utf8"); const file = Bun.file(ruleConfig.filePath);
} catch { const content = await file.text();
file = `# ${slug.toUpperCase()} REEGLID\n\nSisu hetkel puudub.`; return {
content,
title: ruleConfig.title,
};
} catch (error) {
console.error(`Error reading rule file for slug ${slug}:`, error);
return null;
}
}
export default async function RulePage({ params }: PageProps) {
const { slug } = await params;
const ruleData = await getRuleContent(slug);
if (!ruleData) {
notFound();
} }
const data = {title: undefined as string | undefined}; const headingStyle = `text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold uppercase italic text-[#2A2C3F] dark:text-[#EEE5E5]`;
return ( return (
<> <div>
<div className="mb-16"> <div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
<h1 <h1 className={`${headingStyle} mt-8 md:mt-16 mb-4`}>
className={`not-prose ${vipnagorgialla.className} font-bold italic uppercase text-[64px] leading-[96px] tracking-[-0.02em] text-[#2A2C3F] dark:text-[#EEE5E5] mx-auto mt-16 mb-6 px-8`} {ruleData.title}
>
{data.title || `${slug.toUpperCase()} REEGLID`}
</h1> </h1>
<div <div className="prose prose-lg dark:prose-invert max-w-none">
className={`mx-auto px-8 font-worksans <ReactMarkdown
[&_ol]:ml-6 components={
[&_ol_ol]:ml-10 {
[&_ol_ol_ol]:ml-14 h1: (props) => (
[&_h2]:font-bold <h1 className="text-3xl md:text-4xl font-bold my-4">
`} {props.children}
</h1>
),
h2: (props) => (
<h2 className="text-2xl md:text-3xl font-semibold my-3">
{props.children}
</h2>
),
ol: (props) => (
<ol className="list-none ml-6 md:text-xl">
{props.children}
</ol>
),
ul: (props) => (
<ul className="list-disc ml-6 md:text-xl">
{props.children}
</ul>
),
p: (props) => <p className="md:text-xl">{props.children}</p>,
} as Components
}
> >
<ReactMarkdown>{file}</ReactMarkdown> {ruleData.content}
</ReactMarkdown>
</div> </div>
</div> </div>
<SectionDivider /> <SectionDivider />
</> </div>
); );
} }
export async function generateStaticParams() {
return Object.keys(rulesMap).map((slug) => ({
slug,
}));
}

@ -3,7 +3,7 @@ import Link from "next/link";
import SectionDivider from "@/components/SectionDivider"; import SectionDivider from "@/components/SectionDivider";
export default function RulesMenu() { export default function RulesMenu() {
const headingStyle = `text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5]`; const headingStyle = `text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] uppercase`;
const boxStyle = `-skew-x-2 md:-skew-x-5 text-white md:px-12 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]`; const boxStyle = `-skew-x-2 md:-skew-x-5 text-white md:px-12 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]`;
@ -14,51 +14,39 @@ export default function RulesMenu() {
return ( return (
<div> <div>
<div className="flex flex-col md:m-16"> <div className="flex flex-col md:m-16">
<h1 className={`${headingStyle} mt-8 md:mt-16`}> <h1 className={`${headingStyle} ml-3 mt-24 md:ml-0 md:mt-16 mb-4 px-4`}>
REEGLID REEGLID
</h1> </h1>
<div className='flex flex-wrap flex-row lg:mt-16 justify-center lg:items-start gap-12 flex-grow mb-8'> <div className="flex flex-wrap flex-row lg:mt-16 justify-center lg:items-start gap-12 flex-grow mb-8">
<Link href="/kodukord"> <Link href="/kodukord">
<div className={`${boxStyle} bg-[#007CAB] py-20`}> <div className={`${boxStyle} bg-[#007CAB] py-20 px-8`}>
<h2 className={`${boxTextStyle}`}> <h2 className={`${boxTextStyle}`}>Kodukord</h2>
Kodukord
</h2>
</div> </div>
</Link> </Link>
<Link href="/reeglid/cs2"> <Link href="/reeglid/cs2">
<div className={`${boxStyle} bg-[#1F5673] py-20`}> <div className={`${boxStyle} bg-[#1F5673] py-20 px-8`}>
<h2 className={`${boxTextStyle}`}> <h2 className={`${boxTextStyle}`}>CS2 reeglid</h2>
CS2 reeglid
</h2>
</div> </div>
</Link> </Link>
<Link href="reeglid/lol"> <Link href="reeglid/lol">
<div className={`${boxStyle} bg-[#007CAB] py-20`}> <div className={`${boxStyle} bg-[#007CAB] py-20 px-8`}>
<h2 className={`${boxTextStyle}`}> <h2 className={`${boxTextStyle}`}>LoL reeglid</h2>
LoL reeglid
</h2>
</div> </div>
</Link> </Link>
{/* Minitourn. link coming soon*/} {/* Minitourn. link coming soon*/}
{/*<Link href="">*/} {/*<Link href="">*/}
<div <div className={`${boxStyle} bg-[#1F5673] py-16 px-8`}>
className={`${boxStyle} bg-[#1F5673] py-16`}> <h2 className={`${boxTextStyle}`}>Miniturniiride reeglid</h2>
<h2 className={`${boxTextStyle}`}>
Miniturniiride reeglid
</h2>
</div> </div>
{/*</Link>*/} {/*</Link>*/}
</div> </div>
</div> </div>
<SectionDivider /> <SectionDivider />
</div> </div>
); );
} }

@ -50,7 +50,7 @@ export default function Home() {
{/* Stream iframe from Twitch */} {/* Stream iframe from Twitch */}
<div className="border-[#1F5673] -ml-0.75 border-l-0 md:border-l-3 border-b-3 h-full pt-0 md:pt-16"> <div className="border-[#1F5673] -ml-0.75 border-l-0 md:border-l-3 border-b-3 h-full pt-0 md:pt-16">
<iframe <iframe
src="https://player.twitch.tv/?channel=shroud&parent=localhost&parent=tipilan.ee" src="https://player.twitch.tv/?channel=tipilan_ee&parent=localhost&parent=tipilan.ee"
height="100%" height="100%"
width="100%" width="100%"
className="w-full h-full min-h-[400px]" className="w-full h-full min-h-[400px]"

@ -25,36 +25,34 @@ export default function Tourney() {
{/* CS2 turniir */} {/* CS2 turniir */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center mx-8 md:mx-16 lg:mx-32 xl:mx-48"> <div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center mx-8 md:mx-16 lg:mx-32 xl:mx-48">
<div className="-skew-x-2 md:-skew-x-5"> <div className="-skew-x-2 md:-skew-x-5">
<h2 className={`${headingStyle}`}> <h2 className={`${headingStyle}`}>CS2 turniir</h2>
CS2 turniir
</h2>
<p className={"text-2xl mb-4 text-neutral-500"}> <p className={"text-2xl mb-4 text-neutral-500"}>
Toimumisaeg veel selgumisel Toimumisaeg veel selgumisel
</p> </p>
<p className="text-balance"> <p className="text-balance">
TipiLANil toimub Eesti ühe suurima auhinnafondiga CS2 turniire juba sel sügisel. Haara kaasa TipiLANil toimub Eesti ühe suurima auhinnafondiga CS2 turniire
sõbrad ja juba sel sügisel. Haara kaasa sõbrad ja saa osa adrenaliinirohkest
saa osa adrenaliinirohkest kogemusest! kogemusest!
</p> </p>
<br /> <br />
<p className="text-balance"> <p className="text-balance">
Auhinnafond on suuruses 5250, mis jaotatakse TOP3 meeskonna vahel ära. Iga tiimiliige saab Auhinnafond on suuruses 5250, mis jaotatakse TOP3 meeskonna vahel
vastavalt ära. Iga tiimiliige saab vastavalt saavutatud kohale auhinnaks kas
saavutatud kohale auhinnaks kas 600, 300 või 150. 600, 300 või 150.
</p> </p>
<br /> <br />
<div className={"flex flex-row flex-wrap gap-8"}> <div className={"flex flex-row flex-wrap gap-8"}>
<Link href="/reeglid/cs2" target="_blank"> <Link href="/reeglid/cs2" target="_blank">
<button <button
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`} className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
> >
LOE REEGLEID LOE REEGLEID
</button> </button>
</Link> </Link>
<a href="https://fienta.com/et/tipilan" target="_blank"> <a href="https://fienta.com/et/tipilan" target="_blank">
<button <button
className={`px-4 py-2 bg-[#007CAB] cursor-pointer ${vipnagorgialla.className} font-bold italic`} className={`px-4 py-2 bg-[#007CAB] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
> >
OSTA PILET OSTA PILET
</button> </button>
@ -90,40 +88,38 @@ export default function Tourney() {
</div> </div>
</div> </div>
<div className="flex-auto text-right -skew-x-2 md:-skew-x-5"> <div className="flex-auto text-right -skew-x-2 md:-skew-x-5">
<h2 className={`${headingStyle}`}> <h2 className={`${headingStyle}`}>LoL turniir</h2>
LoL turniir
</h2>
<p className={"text-2xl mb-4 text-neutral-500"}> <p className={"text-2xl mb-4 text-neutral-500"}>
Toimumisaeg veel selgumisel Toimumisaeg veel selgumisel
</p> </p>
<p className="text-balance"> <p className="text-balance">
TipiLANil toimub Eesti ühe suurima auhinnafondiga LoL turniire juba sel sügisel. TipiLANil toimub Eesti ühe suurima auhinnafondiga LoL turniire
Haara kaasa sõbrad ja saa osa adrenaliinirohkest kogemusest! juba sel sügisel. Haara kaasa sõbrad ja saa osa adrenaliinirohkest
kogemusest!
</p> </p>
<br /> <br />
<p className="text-balance"> <p className="text-balance">
Auhinnafond on suuruses 3500, mis jaotatakse TOP3 meeskonna vahel ära. Iga tiimiliige saab Auhinnafond on suuruses 3500, mis jaotatakse TOP3 meeskonna vahel
vastavalt saavutatud kohale auhinnaks kas 400, 200 või 100. ära. Iga tiimiliige saab vastavalt saavutatud kohale auhinnaks kas
400, 200 või 100.
</p> </p>
<br /> <br />
<div className="flex flex-row flex-wrap gap-4 md:gap-8 justify-end"> <div className="flex flex-row flex-wrap gap-4 md:gap-8 justify-end">
<Link href="/kodukord" target="_blank"> <Link href="/kodukord" target="_blank">
<button <button
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`} className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
> >
LOE REEGLEID LOE REEGLEID
</button> </button>
</Link> </Link>
<a href="https://fienta.com/et/tipilan" target="_blank"> <a href="https://fienta.com/et/tipilan" target="_blank">
<button <button
className={`px-4 py-2 bg-[#007CAB] cursor-pointer ${vipnagorgialla.className} font-bold italic`} className={`px-4 py-2 bg-[#007CAB] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
> >
OSTA PILET OSTA PILET
</button> </button>
</a> </a>
</div> </div>
</div> </div>
</div> </div>
@ -132,33 +128,33 @@ export default function Tourney() {
{/* Mini-turniirid */} {/* Mini-turniirid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center mx-8 md:mx-16 lg:mx-32 xl:mx-48"> <div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center mx-8 md:mx-16 lg:mx-32 xl:mx-48">
<div className="-skew-x-2 md:-skew-x-5"> <div className="-skew-x-2 md:-skew-x-5">
<h2 className={`${headingStyle}`}> <h2 className={`${headingStyle}`}>Mini&shy;turniirid</h2>
Mini&shy;turniirid
</h2>
<p className={"text-2xl mb-4 text-neutral-500"}> <p className={"text-2xl mb-4 text-neutral-500"}>
Toimumisaeg veel selgumisel Toimumisaeg veel selgumisel
</p> </p>
<p className="text-balance"> <p className="text-balance">
TipiLANil toimub mitmeid erinevaid lõbusaid ja võistlushimu tekitavaid miniturniire. TipiLANil toimub mitmeid erinevaid lõbusaid ja võistlushimu
Miniturniirid toimuvad järgnevates mängudes: SimRacing, Tekken, FIFA, Minecraft Bedwars, tekitavaid miniturniire. Miniturniirid toimuvad järgnevates
Buckshot Roulette, LostGamer ja palju muud. mängudes: SimRacing, Tekken, FIFA, Minecraft Bedwars, Buckshot
Roulette, LostGamer ja palju muud.
</p> </p>
<br /> <br />
<p className="text-balance"> <p className="text-balance">
Auhinnafond on kõigi turniiride peale 1250 ja reeglina saab rahalise auhinna miniturniiri võitja. Auhinnafond on kõigi turniiride peale 1250 ja reeglina saab
rahalise auhinna miniturniiri võitja.
</p> </p>
<br /> <br />
<div className="flex flex-row flex-wrap gap-4 md:gap-8"> <div className="flex flex-row flex-wrap gap-4 md:gap-8">
<Link href="/kodukord" target="_blank"> <Link href="/kodukord" target="_blank">
<button <button
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`} className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
> >
LOE REEGLEID LOE REEGLEID
</button> </button>
</Link> </Link>
<a href="https://fienta.com/et/tipilan" target="_blank"> <a href="https://fienta.com/et/tipilan" target="_blank">
<button <button
className={`px-4 py-2 bg-[#007CAB] cursor-pointer ${vipnagorgialla.className} font-bold italic`} className={`px-4 py-2 bg-[#007CAB] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
> >
OSTA PILET OSTA PILET
</button> </button>

@ -1,8 +1,8 @@
import * as React from "react" import * as React from "react";
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const buttonVariants = cva( const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
@ -32,8 +32,8 @@ const buttonVariants = cva(
variant: "default", variant: "default",
size: "default", size: "default",
}, },
} },
) );
function Button({ function Button({
className, className,
@ -43,17 +43,25 @@ function Button({
...props ...props
}: React.ComponentProps<"button"> & }: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & { VariantProps<typeof buttonVariants> & {
asChild?: boolean asChild?: boolean;
}) { }) {
const Comp = asChild ? Slot : "button" if (asChild) {
return (
<Slot
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...(props as React.ComponentProps<typeof Slot>)}
/>
);
}
return ( return (
<Comp <button
data-slot="button" data-slot="button"
className={cn(buttonVariants({ variant, size, className }))} className={cn(buttonVariants({ variant, size, className }))}
{...props} {...props}
/> />
) );
} }
export { Button, buttonVariants } export { Button, buttonVariants };

@ -0,0 +1,5 @@
## Tulekul
CS2 turniiri reeglid on hetkel ettevalmistamisel ja avaldatakse peagi.
Jälgige meie discordi!
Loading…
Cancel
Save