fix(web): kategori ağacını eski yapısına döndür + toggle tree ikonunu anında tepki verir yap

- category-tree: kılavuz çizgileri + grid-rows aç/kapa animasyonu geri alındı,
  orijinal smooth yapı (paddingLeft girinti) restore edildi
- TreeViewIcon: 80ms gecikmeli/2px küçük hareket yerine gövdeden gecikmesiz
  scaleX büyüme (tree-grow keyframe, origin-left); grid/list ile aynı anında tepki

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 01:09:40 +03:00
parent 89da489532
commit 3fe1201516
3 changed files with 52 additions and 36 deletions

View File

@@ -40,6 +40,7 @@ export function CategoryTree({
key={cat.id} key={cat.id}
category={cat} category={cat}
vehicleId={vehicleId} vehicleId={vehicleId}
level={0}
parentPrefetching={false} parentPrefetching={false}
catalogMode={catalogMode} catalogMode={catalogMode}
brandName={brandName} brandName={brandName}
@@ -53,6 +54,7 @@ export function CategoryTree({
function CategoryNode({ function CategoryNode({
category, category,
vehicleId, vehicleId,
level,
parentPrefetching, parentPrefetching,
catalogMode, catalogMode,
brandName, brandName,
@@ -60,6 +62,7 @@ function CategoryNode({
}: { }: {
category: Category; category: Category;
vehicleId: string; vehicleId: string;
level: number;
parentPrefetching: boolean; parentPrefetching: boolean;
catalogMode?: boolean; catalogMode?: boolean;
brandName?: string; brandName?: string;
@@ -164,6 +167,8 @@ function CategoryNode({
category.unavailable && "pointer-events-none opacity-40", category.unavailable && "pointer-events-none opacity-40",
); );
const indentStyle = { paddingLeft: `${level * 16 + 12}px` };
const rowInner = ( const rowInner = (
<> <>
<span aria-hidden className="flex h-5 w-5 shrink-0 items-center justify-center"> <span aria-hidden className="flex h-5 w-5 shrink-0 items-center justify-center">
@@ -184,7 +189,7 @@ function CategoryNode({
name={category.name} name={category.name}
shimmer={isShimmering} shimmer={isShimmering}
/> />
<span className="min-w-0 flex-1 break-words leading-snug">{category.name}</span> <span className="min-w-0 flex-1 truncate">{category.name}</span>
{category.partCount != null && category.partCount > 0 && ( {category.partCount != null && category.partCount > 0 && (
<span className="shrink-0 tabular-nums text-xs text-muted-foreground"> <span className="shrink-0 tabular-nums text-xs text-muted-foreground">
{category.partCount} {category.partCount}
@@ -209,38 +214,35 @@ function CategoryNode({
} }
search={catalogMode && variantSearch ? variantSearch : undefined} search={catalogMode && variantSearch ? variantSearch : undefined}
className={cn(rowClass, "no-underline")} className={cn(rowClass, "no-underline")}
style={indentStyle}
> >
{rowInner} {rowInner}
</Link> </Link>
) : ( ) : (
<button type="button" onClick={handleExpand} aria-expanded={expanded} className={rowClass}> <button
type="button"
onClick={handleExpand}
aria-expanded={expanded}
className={rowClass}
style={indentStyle}
>
{rowInner} {rowInner}
</button> </button>
)} )}
{hasChildren && ( {hasChildren && expanded && (
// Yumuşak aç/kapa: grid-rows 0fr↔1fr (içerik yüksekliği bilmeye gerek yok, JS animasyon yok) <div>
<div {children.map((child) => (
className={cn( <CategoryNode
"grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none", key={child.id}
expanded ? "grid-rows-[1fr]" : "grid-rows-[0fr]", category={child}
)} vehicleId={vehicleId}
> level={level + 1}
<div className="overflow-hidden"> parentPrefetching={prefetching}
{/* Girinti + kılavuz çizgisi: çizgi parent satırın chevron merkezine (≈22px) hizalanır */} catalogMode={catalogMode}
<div className="ml-[22px] border-l border-border"> brandName={brandName}
{children.map((child) => ( variantSearch={variantSearch}
<CategoryNode />
key={child.id} ))}
category={child}
vehicleId={vehicleId}
parentPrefetching={prefetching}
catalogMode={catalogMode}
brandName={brandName}
variantSearch={variantSearch}
/>
))}
</div>
</div>
</div> </div>
)} )}
</div> </div>

View File

@@ -75,8 +75,8 @@ export function ListViewIcon({ isActive, className }: ViewIconProps) {
); );
} }
// ── Tree → kök + iki dal; aktifken dallar gövdeden dışarı kayar ── // ── Tree → kök + iki dal; aktifte dallar gövdeden anında "büyüyerek" çıkar ──
// Diğer ikonlar gibi transition tabanlı (anında tepki); dallar her zaman görünür. // Gecikme yok, dallar her zaman görünür: tıklama anında belirgin tepki verir.
export function TreeViewIcon({ isActive, className }: ViewIconProps) { export function TreeViewIcon({ isActive, className }: ViewIconProps) {
return ( return (
<span className={cn("relative inline-block h-4 w-4", className)} aria-hidden="true"> <span className={cn("relative inline-block h-4 w-4", className)} aria-hidden="true">
@@ -90,17 +90,15 @@ export function TreeViewIcon({ isActive, className }: ViewIconProps) {
className="absolute rounded bg-current/50" className="absolute rounded bg-current/50"
style={{ left: 3, top: 5, width: 1.5, height: 7.5 }} style={{ left: 3, top: 5, width: 1.5, height: 7.5 }}
/> />
{/* iki çocuk dal: dirsek + düğüm */} {/* iki çocuk dal: dirsek + düğüm — origin-left ile gövdeden büyür */}
{[0, 1].map((i) => ( {[0, 1].map((i) => (
<span <span
key={i} key={i}
className="absolute inline-flex items-center transition-transform duration-[340ms] ease-out motion-reduce:transition-none" className={cn(
style={{ "absolute inline-flex origin-left items-center",
left: 3, isActive && "animate-tree-grow",
top: i === 0 ? 6.25 : 10.25, )}
transform: isActive ? "translateX(0)" : "translateX(-2px)", style={{ left: 3, top: i === 0 ? 6.25 : 10.25 }}
transitionDelay: `${i * 80}ms`,
}}
> >
<span className="rounded bg-current/50" style={{ width: 3.5, height: 1.5 }} /> <span className="rounded bg-current/50" style={{ width: 3.5, height: 1.5 }} />
<span <span

View File

@@ -168,6 +168,18 @@
} }
} }
@keyframes tree-grow {
0% {
transform: scaleX(0.35);
}
70% {
transform: scaleX(1.05);
}
100% {
transform: scaleX(1);
}
}
@keyframes icon-pop { @keyframes icon-pop {
0% { 0% {
opacity: 0; opacity: 0;
@@ -221,6 +233,10 @@
.animate-icon-pop { .animate-icon-pop {
animation: icon-pop 0.32s cubic-bezier(0.16, 1, 0.3, 1); animation: icon-pop 0.32s cubic-bezier(0.16, 1, 0.3, 1);
} }
.animate-tree-grow {
animation: tree-grow 0.24s cubic-bezier(0.16, 1, 0.3, 1);
transform-origin: left center;
}
.carousel-track:hover .animate-scroll-left { .carousel-track:hover .animate-scroll-left {
animation-play-state: paused; animation-play-state: paused;
} }