Update padding in Parallax component for improved layout consistency
Some checks failed
Deploy / deploy (push) Has been cancelled

This commit is contained in:
bilalgursen
2026-07-30 03:54:31 +03:00
parent dc6d894a37
commit 2c542e7756
22 changed files with 274 additions and 133 deletions

Binary file not shown.

View File

@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import Link from "next/link";
import { redirect } from "next/navigation";
import { getSession } from "@/lib/session";
import { PixelCorner } from "@/components/pixel-decor";
import { GirisForm } from "./giris-form";
import { GirisBaslik } from "./giris-baslik";
@@ -27,24 +28,35 @@ export default async function GirisPage({
return (
<div className="flex min-h-screen flex-col bg-slate-50 text-slate-900">
<main className="mx-auto flex w-full max-w-md flex-1 flex-col items-stretch justify-center px-4 py-16">
<div className="rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
<GirisBaslik funnel={funnel} />
<p className="mt-2 text-sm text-slate-600">
{funnel ? (
<>
Seçimlerin hazır giriş yap,{" "}
<span className="font-semibold">5 deneme kredinle</span> listen
hemen üretilsin.
</>
) : (
<>
Kayıt olduğunda{" "}
<span className="font-semibold">5 deneme kredisi</span> hesabına
tanımlanır AI danışmana ücretsiz soru sorabilirsin.
</>
)}
</p>
<GirisForm callbackURL={callbackURL} />
<div className="relative">
<div className="relative rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
<GirisBaslik funnel={funnel} />
<p className="mt-2 text-sm text-slate-600">
{funnel ? (
<>
Seçimlerin hazır giriş yap,{" "}
<span className="font-semibold">5 deneme kredinle</span> listen
hemen üretilsin.
</>
) : (
<>
Kayıt olduğunda{" "}
<span className="font-semibold">5 deneme kredisi</span>{" "}
hesabına tanımlanır AI danışmana ücretsiz soru sorabilirsin.
</>
)}
</p>
<GirisForm callbackURL={callbackURL} />
</div>
<div
aria-hidden
className="pointer-events-none absolute inset-0 z-10 text-primary"
>
<PixelCorner className="absolute -right-4 -top-4 size-9 [&_rect]:[fill-opacity:0.65]" />
<PixelCorner className="absolute -bottom-4 -right-4 size-9 rotate-90 [&_rect]:[fill-opacity:0.65]" />
<PixelCorner className="absolute -bottom-4 -left-4 size-9 rotate-180 [&_rect]:[fill-opacity:0.65]" />
<PixelCorner className="absolute -left-4 -top-4 size-9 -rotate-90 [&_rect]:[fill-opacity:0.65]" />
</div>
</div>
<p className="mt-6 text-center text-xs leading-relaxed text-slate-500">
Giriş yaparak verilerinin tercih önerileri üretmek amacıyla{" "}

View File

@@ -1,7 +1,6 @@
import type { Metadata } from "next";
import Link from "next/link";
import { Parallax } from "@/components/parallax";
import { ScrollFlow } from "@/components/scroll-flow";
export const metadata: Metadata = {
title: "Gizlilik ve KVKK — KolayTercih",
@@ -10,8 +9,7 @@ export const metadata: Metadata = {
// NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir.
export default function GizlilikPage() {
return (
<ScrollFlow>
<main className="mx-auto w-full max-w-2xl px-4 py-16">
<main className="mx-auto w-full max-w-2xl px-4 py-16">
<Parallax fromStart strength={10}>
<h1 className="font-heading text-3xl font-bold">
Gizlilik ve Kişisel Verilerin Korunması
@@ -73,7 +71,6 @@ export default function GizlilikPage() {
</p>
</section>
</Parallax>
</main>
</ScrollFlow>
</main>
);
}

View File

@@ -1,7 +1,6 @@
import type { Metadata } from "next";
import { Mail } from "lucide-react";
import { Parallax } from "@/components/parallax";
import { ScrollFlow } from "@/components/scroll-flow";
export const metadata: Metadata = {
title: "İletişim — KolayTercih",
@@ -9,8 +8,7 @@ export const metadata: Metadata = {
export default function IletisimPage() {
return (
<ScrollFlow>
<main className="mx-auto w-full max-w-2xl px-4 py-16">
<main className="mx-auto w-full max-w-2xl px-4 py-16">
<Parallax fromStart strength={10}>
<h1 className="font-heading text-3xl font-bold">İletişim</h1>
</Parallax>
@@ -30,7 +28,6 @@ export default function IletisimPage() {
hesap adresini eklersen daha hızlı yardımcı olabiliriz. Kart bilgini
asla e-postayla paylaşma.
</p>
</main>
</ScrollFlow>
</main>
);
}

View File

@@ -1,7 +1,6 @@
import type { Metadata } from "next";
import Link from "next/link";
import { Parallax } from "@/components/parallax";
import { ScrollFlow } from "@/components/scroll-flow";
export const metadata: Metadata = {
title: "Kullanım ve Satış Koşulları — KolayTercih",
@@ -10,8 +9,7 @@ export const metadata: Metadata = {
// NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir.
export default function KosullarPage() {
return (
<ScrollFlow>
<main className="mx-auto w-full max-w-2xl px-4 py-16">
<main className="mx-auto w-full max-w-2xl px-4 py-16">
<Parallax fromStart strength={10}>
<h1 className="font-heading text-3xl font-bold">
Kullanım ve Satış Koşulları
@@ -80,7 +78,6 @@ export default function KosullarPage() {
</p>
</section>
</Parallax>
</main>
</ScrollFlow>
</main>
);
}

View File

@@ -7,6 +7,9 @@ import { ListeCekmecesi } from "@/components/manuel-liste/liste-cekmecesi";
import { HeaderGate } from "@/components/header-gate";
import { ProgressiveBlur } from "@/components/ui/skiper-ui/skiper41";
import { DevPanel } from "@/components/dev/dev-panel";
import { ScrollFlow } from "@/components/scroll-flow";
import { PagePixelLayer } from "@/components/page-pixel-layer";
import { ConsoleAsr } from "@/components/console-asr";
import "./globals.css";
const calSans = Cal_Sans({
@@ -54,6 +57,8 @@ export default function RootLayout({
className={`${calSans.variable} ${clarityCity.variable} ${geistMono.variable} ${bricolage.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col bg-slate-50">
<ConsoleAsr />
<PagePixelLayer />
{/* Site geneli progressive blur — üst ve alt */}
<div className="pointer-events-none fixed inset-x-0 top-0 z-40 h-28">
<ProgressiveBlur
@@ -74,7 +79,9 @@ export default function RootLayout({
<HeaderGate>
<SiteHeader />
</HeaderGate>
{children}
<ScrollFlow className="flex min-h-0 flex-1 flex-col">
{children}
</ScrollFlow>
{/* Manuel 24'lük liste çekmecesi + "+" uçuş katmanı (navbar'dan açılır) */}
<ListeCekmecesi />
<Toaster position="top-center" />

View File

@@ -24,6 +24,7 @@ import type { RaporParams } from "@/lib/rapor-havuzu";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { RaporListesi } from "@/components/rapor-listesi";
import { ViewportPortal } from "@/components/viewport-portal";
import { raporMaskele, ACIK_SATIR } from "@/lib/rapor-maske";
import { SohbetClient } from "./sohbet-client";
import { RevizyonKutusu } from "./revizyon-kutusu";
@@ -322,13 +323,15 @@ export default async function ListemPage({
{/* Mobilde sohbet sağ rayla birlikte en alta düşer; yüzen kısayol
kullanıcıyı 24 satırı kaydırmadan danışmana ulaştırır. */}
<a
href="#sohbet"
className="fixed bottom-5 right-4 z-40 inline-flex h-11 items-center gap-2 rounded-full bg-primary px-4 text-sm font-semibold text-white shadow-lg transition-transform duration-200 active:scale-[0.97] lg:hidden print:hidden"
>
<MessageCircleQuestion className="size-4" aria-hidden />
Danışmana sor
</a>
<ViewportPortal>
<a
href="#sohbet"
className="fixed bottom-5 right-4 z-40 inline-flex h-11 items-center gap-2 rounded-full bg-primary px-4 text-sm font-semibold text-white shadow-lg transition-transform duration-200 active:scale-[0.97] lg:hidden print:hidden"
>
<MessageCircleQuestion className="size-4" aria-hidden />
Danışmana sor
</a>
</ViewportPortal>
</main>
);
}

View File

@@ -11,9 +11,9 @@ import {
} from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { HeroFocusButton } from "@/components/hero-focus-button";
import { HeroForm } from "@/components/hero-form";
import { Parallax } from "@/components/parallax";
import { ScrollFlow } from "@/components/scroll-flow";
import { Reveal } from "@/components/landing/reveal";
import { TurkeyPixelMap } from "@/components/turkey-pixel-map";
import {
@@ -24,7 +24,6 @@ import {
} from "@/components/pixel-decor";
import { URUNLER } from "@/lib/credits";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Accordion,
AccordionContent,
@@ -166,8 +165,10 @@ function ComparisonCell({ value }: { value: boolean | string }) {
export default function Home() {
return (
<div className="bg-slate-50 text-slate-900">
<ScrollFlow className="flex min-h-screen flex-col">
<div
id="top"
className="flex min-h-screen flex-col bg-slate-50 text-slate-900"
>
<main className="flex-1">
{/* Hero */}
<section className="relative isolate overflow-hidden">
@@ -193,7 +194,7 @@ export default function Home() {
<Parallax
fromStart
strength={14}
className="mx-auto flex min-h-[calc(100svh-5.5rem)] max-w-6xl flex-col items-center justify-center px-4 pb-44 pt-4 text-center"
className="mx-auto flex min-h-[calc(100svh-5.5rem)] max-w-6xl flex-col items-center justify-center px-4 py-4 text-center sm:pb-44 sm:pt-4"
>
<Badge
variant="secondary"
@@ -201,7 +202,7 @@ export default function Home() {
>
2026 YKS tercih dönemi ık
</Badge>
<h1 className="max-w-3xl font-heading text-4xl font-bold tracking-[0.015em] sm:text-6xl">
<h1 className="max-w-3xl font-heading text-7xl font-bold tracking-[0.015em] sm:text-6xl">
Dört yıllık geleceğini <br className="hidden sm:block" />
<span className="font-bricolage italic tracking-tighter font-bold text-primary">
48 saatlik panikle
@@ -481,7 +482,7 @@ export default function Home() {
value={faq.question}
className="last:border-b-0"
>
<AccordionTrigger className="cursor-pointer text-left font-heading text-base font-semibold">
<AccordionTrigger className="cursor-pointer text-left font-sans text-base font-semibold">
{faq.question}
</AccordionTrigger>
<AccordionContent className="text-base leading-relaxed text-slate-600">
@@ -522,13 +523,7 @@ export default function Home() {
Sıralamanı gir, yapay zekâ danışmanın veriyle konuşsun.
Tercih dönemi bitmeden yerini al.
</p>
<Button
asChild
size="lg"
className="mt-8 h-12 cursor-pointer bg-orange-500 px-8 text-white transition-colors duration-200 hover:bg-orange-600"
>
<a href="#hero-form">Ücretsiz dene</a>
</Button>
<HeroFocusButton />
</div>
</div>
</Reveal>
@@ -571,7 +566,6 @@ export default function Home() {
</nav>
</div>
</footer>
</ScrollFlow>
</div>
);
}

View File

@@ -14,7 +14,6 @@ import { URUNLER, DENEME_KREDISI } from "@/lib/credits";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Parallax } from "@/components/parallax";
import { ScrollFlow } from "@/components/scroll-flow";
import { SatinAlForm } from "./satin-al-form";
export const metadata: Metadata = {
@@ -53,8 +52,7 @@ export default async function PaketPage({
const odemeHatasi = hata === "token" || hata === "siparis";
return (
<div className="bg-slate-50 text-slate-900">
<ScrollFlow className="flex min-h-screen flex-col">
<div className="flex min-h-screen flex-col bg-slate-50 text-slate-900">
<main className="mx-auto w-full max-w-3xl flex-1 px-4 py-16">
{odemeHatasi ? (
<div
@@ -189,7 +187,6 @@ export default async function PaketPage({
.
</p>
</main>
</ScrollFlow>
</div>
);
}

View File

@@ -8,6 +8,7 @@ import { animate } from "motion";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { ViewportPortal } from "@/components/viewport-portal";
import type { SihirbazFacetleri } from "@/lib/db";
import {
SIHIRBAZ_STORAGE_KEY,
@@ -446,54 +447,56 @@ export function SihirbazBolumu({
</div>
{/* Sabit alt çağrı kartı — liste/panel yokken ve kart kapatılmadıysa */}
{monteEdildi && !panelde && !kartGizle && !acik ? (
<div className="pointer-events-none fixed inset-x-0 bottom-4 z-40 px-4">
<div className="pointer-events-auto mx-auto flex max-w-xl items-center gap-4 rounded-2xl border border-slate-200 bg-white p-4 shadow-xl">
<div className="flex-1">
<p className="font-heading text-sm font-bold text-slate-900">
{girisBekliyor
? "Seçimlerin kayıtlı — AI listen hazır bekliyor"
: "3 adımda 24 tercihlik listen"}
</p>
<p className="text-xs text-slate-500">
{girisBekliyor
? "Ücretsiz giriş yap, 5 deneme kredinle listen otomatik kurulsun."
: "Sıralamana uygun bölümleri seç, yapay zekâ listeni kursun."}
</p>
<ViewportPortal>
{monteEdildi && !panelde && !kartGizle && !acik ? (
<div className="pointer-events-none fixed inset-x-0 bottom-4 z-40 px-4">
<div className="pointer-events-auto mx-auto flex max-w-xl items-center gap-4 rounded-2xl border border-slate-200 bg-white p-4 shadow-xl">
<div className="flex-1">
<p className="font-heading text-sm font-bold text-slate-900">
{girisBekliyor
? "Seçimlerin kayıtlı — AI listen hazır bekliyor"
: "3 adımda 24 tercihlik listen"}
</p>
<p className="text-xs text-slate-500">
{girisBekliyor
? "Ücretsiz giriş yap, 5 deneme kredinle listen otomatik kurulsun."
: "Sıralamana uygun bölümleri seç, yapay zekâ listeni kursun."}
</p>
</div>
{girisBekliyor ? (
<Button
asChild
size="sm"
className="cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
>
<Link href={girisUrl}>Giriş yap</Link>
</Button>
) : (
<Button
size="sm"
onClick={() => acikDegisti(true)}
className="cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
>
Başla
</Button>
)}
<button
type="button"
aria-label="Kapat"
onClick={() => {
setKartGizle(true);
try {
sessionStorage.setItem(KAPATMA_KEY, "1");
} catch {}
}}
className="cursor-pointer rounded-full p-1 text-slate-400 transition-colors duration-200 hover:bg-slate-100 hover:text-slate-600"
>
<X className="size-4" aria-hidden />
</button>
</div>
{girisBekliyor ? (
<Button
asChild
size="sm"
className="cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
>
<Link href={girisUrl}>Giriş yap</Link>
</Button>
) : (
<Button
size="sm"
onClick={() => acikDegisti(true)}
className="cursor-pointer bg-orange-500 text-white transition-colors duration-200 hover:bg-orange-600"
>
Başla
</Button>
)}
<button
type="button"
aria-label="Kapat"
onClick={() => {
setKartGizle(true);
try {
sessionStorage.setItem(KAPATMA_KEY, "1");
} catch {}
}}
className="cursor-pointer rounded-full p-1 text-slate-400 transition-colors duration-200 hover:bg-slate-100 hover:text-slate-600"
>
<X className="size-4" aria-hidden />
</button>
</div>
</div>
) : null}
) : null}
</ViewportPortal>
{/* Üretim başladığında modalı kaldır; yeni panelin geçişini Motion
üstlenir ve odağı panel içeriğine taşır. */}

View File

@@ -8,6 +8,7 @@ import { useEffect, useState } from "react";
import { MessageCircleMore, X } from "lucide-react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import { SohbetClient } from "@/app/listem/sohbet-client";
import { ViewportPortal } from "@/components/viewport-portal";
export function SohbetPopup({ kredi }: { kredi: number }) {
const [acik, setAcik] = useState(false);
@@ -23,7 +24,7 @@ export function SohbetPopup({ kredi }: { kredi: number }) {
}, [acik]);
return (
<>
<ViewportPortal>
<AnimatePresence>
{acik ? (
<motion.div
@@ -68,6 +69,6 @@ export function SohbetPopup({ kredi }: { kredi: number }) {
)}
{acik ? "Kapat" : "Danışmana sor"}
</button>
</>
</ViewportPortal>
);
}

View File

@@ -0,0 +1,33 @@
"use client";
import { useEffect } from "react";
let hasPlayed = false;
const consoleMessage = [
"%cبِسْمِ اللَّهِ الرَّحْمٰنِ الرَّحِيمِ",
"",
"وَالْعَصْرِۙ اِنَّ الْاِنْسَانَ لَف۪ي خُسْرٍۙ",
"اِلَّا الَّذ۪ينَ اٰمَنُوا وَعَمِلُوا الصَّالِحَاتِ",
"وَتَوَاصَوْا بِالْحَقِّ وَتَوَاصَوْا بِالصَّبْرِ",
"",
"Rahmân ve Rahîm olan Allahın adıyla.",
"",
"Asra yemîn olsun ki,",
"İnsan gerçekten ziyândadır.",
"Ancak iman edip sâlih ameller yapanlar, birbirlerine hakkı tavsiye",
"edenler ve birbirlerine sabretmeyi öğütleyenler müstesnâ!",
].join("\n");
export function ConsoleAsr() {
useEffect(() => {
if (hasPlayed) return;
hasPlayed = true;
window.setTimeout(() => {
console.log(consoleMessage, "font-size:16px");
}, 250);
}, []);
return null;
}

View File

@@ -200,7 +200,10 @@ export function DevPanel() {
</button>
</div>
<div className="flex flex-col gap-4 overflow-y-auto p-4 text-sm">
<div
data-lenis-prevent
className="flex flex-col gap-4 overflow-y-auto p-4 text-sm"
>
{/* Oturum */}
<section>
<p className="text-[11px] font-semibold uppercase tracking-wide text-slate-400">

View File

@@ -0,0 +1,26 @@
"use client";
import { Button } from "@/components/ui/button";
export function HeroFocusButton() {
function handleClick() {
const input = document.getElementById("siralama");
if (input instanceof HTMLInputElement) {
input.focus({ preventScroll: true });
}
window.scrollTo({ top: 0, behavior: "smooth" });
}
return (
<Button
type="button"
size="lg"
onClick={handleClick}
className="mt-8 h-12 cursor-pointer bg-orange-500 px-8 text-white transition-colors duration-200 hover:bg-orange-600"
>
Ücretsiz dene
</Button>
);
}

View File

@@ -132,7 +132,7 @@ export function HeroForm() {
// flex-1 yalnızca yatay dizilimde (sm+): mobilde form flex-col olduğu
// için flex-1 inputun yüksekliğini eziyordu. Mobilde geniş padding
// + büyük yazı; sm ve üzeri eski görünüm.
className="h-12 bg-background px-5 text-lg sm:flex-1 sm:px-2.5 sm:text-base"
className="h-14 bg-background px-6 py-3 text-lg sm:h-12 sm:flex-1 sm:px-2.5 sm:py-1 sm:text-base"
/>
<Button
type="submit"

View File

@@ -206,6 +206,7 @@ function CekmeceSheet() {
layout="position"
layoutScroll
as="ol"
data-lenis-prevent
className="min-h-0 flex-1 space-y-2 overflow-x-hidden overflow-y-auto px-4 py-4"
>
<AnimatePresence initial={false}>

View File

@@ -0,0 +1,46 @@
"use client";
import { usePathname } from "next/navigation";
import { PixelCorner } from "@/components/pixel-decor";
const pixelClass = "size-10 [&_rect]:[fill-opacity:0.55]";
/**
* Ana sayfanın kendi zengin piksel kompozisyonunu tekrar etmeden, iç
* sayfalara ortak bir piksel çerçevesi taşır. Katman içeriklerin üstünde
* kaldığı için sayfaların opak zeminlerinde de görünür; kenarlara
* yerleştiğinden etkileşimleri ve okunabilirliği engellemez.
*/
export function PagePixelLayer() {
const pathname = usePathname();
if (pathname === "/") return null;
return (
<div
aria-hidden
className="pointer-events-none fixed inset-0 z-30 overflow-hidden print:hidden"
>
<PixelCorner
className={`absolute -left-3 top-28 -rotate-90 ${pixelClass}`}
/>
<PixelCorner
className={`absolute -right-3 top-28 ${pixelClass}`}
/>
<PixelCorner
className={`absolute -left-4 top-[46%] -rotate-90 text-orange-500 max-sm:hidden ${pixelClass}`}
/>
<PixelCorner
className={`absolute -right-4 top-[58%] rotate-90 max-sm:hidden ${pixelClass}`}
/>
<PixelCorner
className={`absolute -bottom-3 left-6 rotate-180 ${pixelClass}`}
/>
<PixelCorner
className={`absolute -bottom-3 right-6 rotate-90 text-orange-500 ${pixelClass}`}
/>
</div>
);
}

View File

@@ -35,24 +35,27 @@ export function ScrollFlow({
const reduceMotion = useReducedMotion();
const { scrollY } = useScroll();
const velocity = useVelocity(scrollY);
// Ham hız titrek olur; yay ile yumuşatınca bükülme salınıp yerine oturur
const smoothVelocity = useSpring(velocity, {
damping: 50,
stiffness: 420,
mass: 0.6,
// Önce hızı kesintisiz bir hedef açıya dönüştür, sonra doğrudan görünen
// açıyı yumuşat. Böylece eşik/clamp kaynaklı ani hız değişimleri yaya girmez.
const targetSkew = useTransform(velocity, (v) => {
const RESPONSE = 1800;
return Math.tanh(v / RESPONSE) * maxSkew;
});
// Ölü bölge: yavaş/ufak kaydırmalarda bükülme tamamen 0 kalır — transform
// hiç değişmediği için compositor'a kare başı iş çıkmaz. Eşik üstünde
// kareyle (ease-in) yükselir, 0,01°'lik adımlara yuvarlanır ki yayın
// mikro-salınımları her karede katmanı kirletmesin.
const skewY = useTransform(smoothVelocity, (v) => {
const DEADZONE = 500; // px/sn — bunun altı "normal gezinme" sayılır
const FULL = 2600; // px/sn — bu hızda maxSkew'e ulaşılır
const mag = Math.abs(v);
if (mag < DEADZONE) return 0;
const t = Math.min(1, (mag - DEADZONE) / (FULL - DEADZONE));
const s = t * t * maxSkew * Math.sign(v);
return Math.round(s * 100) / 100;
const smoothSkew = useSpring(targetSkew, {
stiffness: 240,
damping: 26,
mass: 0.7,
restDelta: 0.005,
restSpeed: 0.01,
});
// Yuvarlama görsel basamaklanma üretir. Tam hassasiyetle çiz; yalnızca açı
// algılanamayacak kadar küçüldüğünde transform'u kaldırarak dev sayfa
// katmanının GPU belleğinde boşuna tutulmasını önle.
const transform = useTransform(smoothSkew, (v) => {
if (Math.abs(v) < 0.01) return "none";
return `skewY(${v}deg)`;
});
if (reduceMotion) {
@@ -61,12 +64,7 @@ export function ScrollFlow({
return (
<ReactLenis root options={{ lerp: 0.09, anchors: true }}>
{/* willChange: bükülme 0'a döndüğünde dev katman GPU'dan düşüp
bir sonraki tekerlekte yeniden rasterize edilmesin */}
<motion.div
className={className}
style={{ skewY, willChange: "transform" }}
>
<motion.div className={className} style={{ transform }}>
{children}
</motion.div>
</ReactLenis>

View File

@@ -26,6 +26,7 @@ function ChatContainerRoot({
return (
<StickToBottom
className={cn("flex overflow-y-auto", className)}
data-lenis-prevent
resize="smooth"
initial="instant"
role="log"

View File

@@ -60,6 +60,7 @@ function DialogContent({
<DialogOverlay />
<DialogPrimitive.Content
data-slot="dialog-content"
data-lenis-prevent
className={cn(
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className

View File

@@ -68,6 +68,7 @@ function SelectContent({
<SelectPrimitive.Portal>
<SelectPrimitive.Content
data-slot="select-content"
data-lenis-prevent
data-align-trigger={position === "item-aligned"}
className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )}
position={position}

View File

@@ -0,0 +1,23 @@
"use client";
import { type ReactNode, useSyncExternalStore } from "react";
import { createPortal } from "react-dom";
const subscribe = () => () => {};
const getClientSnapshot = () => true;
const getServerSnapshot = () => false;
/**
* Viewport'a sabit kalması gereken öğeleri, transform uygulanan sayfa
* katmanının dışına taşır. Böylece üst katmandaki scroll skew animasyonu
* `position: fixed` koordinat sistemini değiştirmez.
*/
export function ViewportPortal({ children }: { children: ReactNode }) {
const mounted = useSyncExternalStore(
subscribe,
getClientSnapshot,
getServerSnapshot,
);
return mounted ? createPortal(children, document.body) : null;
}