perf: harita/tablo re-render'ları + content-visibility (Faz 6)
- tercih-haritasi: viewBox animasyonu transient — kare başına setState (380ms × ~23 render × 81 path) yerine svg.setAttribute; il path ref callback'leri key başına stabil; il özetleri/şerit/pin grupları useMemo; reduce×3+baskinRisk tek geçiş; çift lookup ve çift uniLogoYoluFromAd kalktı - liste-store: O(1) idSet + useListedeMi/useListeDoluMu primitive selector'ları; program-ekle-butonu artık tüm listeye abone değil — 50 satırlık tabloda mutasyon başına 50 yerine yalnız değişen buton render olur - bolum-program-listesi: decorate-sort-undecorate yerine stable toSorted; sıralama/sayfalama startTransition'da - il-secim-haritasi: normalizeIlAdi modül seviyesinde precompute (render başına ~1000 string normalizasyonu vardı); Map/Set/sort useMemo - kt-cv-satir utility: uzun tablo satırlarında content-visibility:auto + contain-intrinsic-size (program/bolum/universite/rapor tabloları) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { startTransition, useMemo, useRef, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { ChevronDown, ChevronUp } from "lucide-react";
|
||||
import type { Program } from "@/types/yokatlas";
|
||||
@@ -104,19 +104,17 @@ export function BolumProgramListesi({
|
||||
const siraliProgramlar = useMemo(() => {
|
||||
if (!aktifAlan) return programlar;
|
||||
|
||||
return programlar
|
||||
.map((program, index) => ({ program, index }))
|
||||
.sort((a, b) => {
|
||||
const aDeger = a.program[aktifAlan];
|
||||
const bDeger = b.program[aktifAlan];
|
||||
if (aDeger == null && bDeger == null) return a.index - b.index;
|
||||
if (aDeger == null) return 1;
|
||||
if (bDeger == null) return -1;
|
||||
|
||||
const fark = aDeger - bDeger;
|
||||
return fark === 0 ? a.index - b.index : yon === "artan" ? fark : -fark;
|
||||
})
|
||||
.map(({ program }) => program);
|
||||
// toSorted stable: eşitlikte orijinal sıra zaten korunur, index
|
||||
// dekorasyonu (N obje allocation + 2 ek geçiş) gereksizdi.
|
||||
return programlar.toSorted((a, b) => {
|
||||
const aDeger = a[aktifAlan];
|
||||
const bDeger = b[aktifAlan];
|
||||
if (aDeger == null && bDeger == null) return 0;
|
||||
if (aDeger == null) return 1;
|
||||
if (bDeger == null) return -1;
|
||||
const fark = aDeger - bDeger;
|
||||
return yon === "artan" ? fark : -fark;
|
||||
});
|
||||
}, [aktifAlan, programlar, yon]);
|
||||
|
||||
const gosterilenSayfa = aktifAlan ? istemciSayfa : sayfa;
|
||||
@@ -129,18 +127,22 @@ export function BolumProgramListesi({
|
||||
[siraliProgramlar, gosterilenSayfa],
|
||||
);
|
||||
|
||||
// Yüzlerce satırın sort+render'ı tıklamayı bloklamasın; buton geri
|
||||
// bildirimi CSS'ten geldiği için tüm güncelleme non-urgent kalabilir.
|
||||
function sirala(alan: SiralamaAlani) {
|
||||
setIstemciSayfa(1);
|
||||
if (aktifAlan === alan) {
|
||||
setYon((mevcut) => (mevcut === "artan" ? "azalan" : "artan"));
|
||||
return;
|
||||
}
|
||||
setAktifAlan(alan);
|
||||
setYon("artan");
|
||||
startTransition(() => {
|
||||
setIstemciSayfa(1);
|
||||
if (aktifAlan === alan) {
|
||||
setYon((mevcut) => (mevcut === "artan" ? "azalan" : "artan"));
|
||||
return;
|
||||
}
|
||||
setAktifAlan(alan);
|
||||
setYon("artan");
|
||||
});
|
||||
}
|
||||
|
||||
function istemciSayfaDegistir(n: number) {
|
||||
setIstemciSayfa(n);
|
||||
startTransition(() => setIstemciSayfa(n));
|
||||
listeRef.current?.scrollIntoView({ block: "start" });
|
||||
}
|
||||
|
||||
@@ -193,7 +195,7 @@ export function BolumProgramListesi({
|
||||
const devlet = p.unitur === "DEVLET";
|
||||
|
||||
return (
|
||||
<TableRow key={p.id}>
|
||||
<TableRow key={p.id} className="kt-cv-satir">
|
||||
<TableCell className="max-w-0 w-full">
|
||||
<span className="flex min-w-0 items-center gap-2.5">
|
||||
{p.universiteSlug ? (
|
||||
|
||||
@@ -66,7 +66,7 @@ export function UniversiteProgramTablosu({
|
||||
{programlar.map((p) => {
|
||||
const taban = programEtkinSira(p);
|
||||
return (
|
||||
<TableRow key={p.id}>
|
||||
<TableRow key={p.id} className="kt-cv-satir">
|
||||
<TableCell className="max-w-0 w-full">
|
||||
{p.bolumSlug ? (
|
||||
<Link
|
||||
|
||||
Reference in New Issue
Block a user