From f752d2710193010cd5306b6ca49abf4afb0fbb6a Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Sun, 9 Aug 2026 03:39:10 +0300 Subject: [PATCH] feat: add pill variant to Tabs component and enhance ProgramTablosu with motion effects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduced a new "pill" variant for the Tabs component, allowing for a rounded, active tab style that aligns with the design of the "Giriş yap" button. Enhanced the ProgramTablosu component by integrating motion effects for active tab indicators, improving user experience with smooth transitions. Updated color handling for active states to ensure better visibility and consistency across the UI. --- src/components/ui/tabs.tsx | 6 ++ .../liste/components/program-tablosu.tsx | 60 ++++++++++++++----- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx index 05f469f..450f222 100644 --- a/src/components/ui/tabs.tsx +++ b/src/components/ui/tabs.tsx @@ -31,6 +31,9 @@ const tabsListVariants = cva( variant: { default: "bg-muted", line: "gap-1 bg-transparent", + // Nav'daki "Giriş yap" pili ile aynı dil: beyaz kapsül içinde koyu + // aktif hap (bkz. user-nav.tsx). Kayan gösterge tüketicide çizilir. + pill: "gap-1 rounded-full border border-slate-200 bg-white p-1 group-data-horizontal/tabs:h-auto", }, }, defaultVariants: { @@ -66,6 +69,9 @@ function TabsTrigger({ "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent", "data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground", + // Pill varyantı: arka planı tüketicideki kayan gösterge verir, trigger + // şeffaf kalır; aktif metin koyu hap üstünde beyaza döner. + "group-data-[variant=pill]/tabs-list:h-auto group-data-[variant=pill]/tabs-list:rounded-full group-data-[variant=pill]/tabs-list:data-active:bg-transparent group-data-[variant=pill]/tabs-list:data-active:text-white", "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100", className )} diff --git a/src/features/liste/components/program-tablosu.tsx b/src/features/liste/components/program-tablosu.tsx index 52afdc0..5f35ec2 100644 --- a/src/features/liste/components/program-tablosu.tsx +++ b/src/features/liste/components/program-tablosu.tsx @@ -7,6 +7,7 @@ import { useMemo, useState } from "react"; import Link from "next/link"; +import { motion, useReducedMotion } from "motion/react"; import { ChevronDown, Rocket, @@ -43,30 +44,31 @@ import { UniLogo } from "@/components/uni-logo"; const DILIMLER: { key: DilimKey; etiket: string; - aciklama: string; icon: typeof Rocket; renk: string; + // Aktif hap koyu (slate-900) — ikon tonu okunurluk için bir kademe açılır + renkAktif: string; }[] = [ { key: "hayal", etiket: "Hayal", - aciklama: "Tabanı sıralamanın üstünde — şansını denediğin satırlar.", icon: Rocket, renk: "text-red-500", + renkAktif: "text-red-400", }, { key: "dengeli", etiket: "Dengeli", - aciklama: "Tabanı sıralamana denk — listenin bel kemiği.", icon: Scale, renk: "text-amber-500", + renkAktif: "text-amber-400", }, { key: "garanti", etiket: "Garanti", - aciklama: "Tabanı belirgin şekilde altında — güvenli bölge.", icon: ShieldCheck, renk: "text-emerald-600", + renkAktif: "text-emerald-400", }, ]; @@ -106,6 +108,7 @@ export function ProgramTablosu({ const [aktifDilim, setAktifDilim] = useState(() => varsayilanDilim(sonuclar), ); + const hareketAzalt = useReducedMotion(); // Sihirbaz seçimlerinden profil çerezine yazılıp SSR'da prop olarak gelen // filtreler — sayfalama isteklerinde ve veri anahtarında kullanılır. const iller = useMemo(() => filtreIller ?? [], [filtreIller]); @@ -180,17 +183,43 @@ export function ProgramTablosu({ onValueChange={(v) => setAktifDilim(v as DilimKey)} > {/* Dilim sekmeleri tablonun hemen üstünde; puan türü burada değil, - navbar'daki sıralama rozetinden değiştirilir. */} - - {DILIMLER.map((d) => ( - - - {d.etiket} - - {sonuclar.toplam[d.key].toLocaleString("tr-TR")} - - - ))} + navbar'daki sıralama rozetinden değiştirilir. Aktif hap, nav'daki + "Giriş yap" butonuyla aynı dilde (koyu, rounded-full) ve layoutId + ile sekmeler arasında kayarak taşınır. */} + + {DILIMLER.map((d) => { + const aktif = aktifDilim === d.key; + return ( + + {aktif ? ( + + ) : null} + + + {d.etiket} + + {sonuclar.toplam[d.key].toLocaleString("tr-TR")} + + + + ); + })} {DILIMLER.map((d) => { @@ -198,7 +227,6 @@ export function ProgramTablosu({ const toplam = sonuclar.toplam[d.key]; return ( -

{d.aciklama}

{satirlar.length === 0 ? (