fix(ui): program ayrıntı çekmecesi — önbellekli açılışta sahne atlanır, sahne ≈1,2 s, istihdam cümlesi büyük harfle
Tasarımcı B1/B3/B5 (7 Eki). Çekmece ikinci kez açılınca AcilisSahnesi hazir=true ile monte oluyor, logo kareleri sayfa ekrana kaymadan oynuyor, gövde ~1 s boş kalıyordu: `hazir` verilmiş bileşende sahne yalnız false → true geçişinde oynar; montajda true ise (önbellek) 0,25 s solma. Verilmezse (akademik göstergeler, sunucudan hazır) her açılışta oynar. Kartsız sahne süresi ≈2 s'den ≈1,2 s'ye: kare ve blok aralığı 0,05 s, netleşme 0,35 s, logo belir/dur/sön 0,28/0,16/0,3 s. "Mezun istihdamı" paragrafının ilk harfi büyük (tr-TR). DESIGN.md §4.19 gerçek sürelerle. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -661,8 +661,14 @@ sihirbaz modalı içindir; yeni bir ayrıntı için `Dialog` açılmaz.
|
||||
gelince başlığın altında üç logo karesi (`data-logo` kabı, `relative mx-auto h-16 w-16`)
|
||||
belirir, kısa durur, sönerek kaybolur (scale 0.6 + opacity 0; kap sıfır boya iner), içerik
|
||||
blokları (`data-sonra`: künye, kontenjan türleri, netler, koşullar, istihdam, üniversite,
|
||||
kaynak) 0,08 s arayla bulanıktan netleşir; toplam ≈ 1 s. Hareketi azalt: logo yok, 0,25 s
|
||||
solma (§6). Akademik göstergeler çekmecesi (§4.9) aynı bileşenin kartlı modunu kullanır.
|
||||
kaynak) 0,05 s arayla bulanıktan netleşir; toplam ≈ 1,2 s (kareler 0,1–0,2 s'de belirir,
|
||||
0,94 s'de sönmüş olur; ilk blok ≈ 0,95 s'de, sonuncusu ≈ 1,3 s'de net — 8 Eki 2026, önceki
|
||||
0,08'lik aralık ≈ 2 s tutuyordu). Sahne yalnız veri ilk geldiğinde (`hazir` false → true)
|
||||
oynar; önbellekli yeniden açılışta sahne atlanır, içerik 0,25 s solmayla gelir — gövde hiçbir
|
||||
an boş kalmaz (iskelet ya da içerik). Hareketi azalt: logo yok, 0,25 s solma (§6). Akademik
|
||||
göstergeler çekmecesi (§4.9) aynı bileşenin kartlı modunu kullanır (her açılışta oynar).
|
||||
Gövde kaydırılınca içerik tutamaç ve çarpının altına maskesiz girmesin diye `AltSayfaGovde`
|
||||
üst kenarında 20 px saydam → dolu `mask-image` var (§4.10; tüm çekmeceler).
|
||||
|
||||
### 4.20 Danışman sohbeti — `sohbet-client.tsx`, `arac-pilleri.tsx`
|
||||
- **Soru formu hazırlanırken** (7 Eki 2026): `soru_sor` aracı tamamlanana dek cevap iskeleti değil
|
||||
|
||||
@@ -10,7 +10,11 @@
|
||||
// `[data-logo]` kabındaki üç `[data-logo-kare]` belirir, kısa durur, sonra
|
||||
// sönerek kaybolur (scale 0.6 + opacity 0; Bilal, 7 Eki 2026) ve
|
||||
// `[data-sonra]` bloklar netleşerek gelir.
|
||||
// "Hareketi azalt"ta logo ve büyüme yok, yalnız 0,25 s solma (§6).
|
||||
// "Hareketi azalt"ta logo ve büyüme yok, yalnız 0,25 s solma (§6). Veri
|
||||
// bekleyen çekmecede (`hazir` verilmiş) sahne yalnız veri ilk geldiğinde
|
||||
// (false → true) oynar; önbellekli yeniden açılışta aynı 0,25 s solma
|
||||
// (8 Eki 2026: ikinci açılışta kareler çekmece ekrana kaymadan oynuyor,
|
||||
// gövde ~1 s boş kalıyordu).
|
||||
|
||||
import { useLayoutEffect, useRef, type ReactNode } from "react";
|
||||
import { animate, useReducedMotion } from "motion/react";
|
||||
@@ -36,15 +40,25 @@ const ICERIK_BASI = 0.95;
|
||||
const GUCLU_CIKIS = [0.23, 1, 0.32, 1] as const;
|
||||
const YERLESME = [0.32, 0.72, 0, 1] as const;
|
||||
|
||||
// Kartsız mod: logo karesi belir → dur → sön. Belirme ve duruş kartlı
|
||||
// modla aynı anlara denk gelir (KARE_SURE·0.24, ·0.52); sönme 0,4 s.
|
||||
const LOGO_SURE = KARE_SURE * KARE_ZAMANLARI[2] + 0.4;
|
||||
// Kartsız mod (saniye): logo karesi belir → dur → sön; kareler 0,05 s arayla
|
||||
// gelir, içerik blokları sönmeyle çaprazlanarak 0,05 s arayla netleşir.
|
||||
// Toplam ≈ 1,2 s: son logo karesi 0,2'de başlar, sönme 0,94'te biter, ilk
|
||||
// blok ≈ 0,95'te net, 7–8 blokla sonuncusu ≈ 1,3'te (8 Eki 2026; önceki
|
||||
// 0,08'lik aralıklarla ≈ 2 s sürüyor, DESIGN.md §4.19 "≈ 1 s" ile çelişiyordu).
|
||||
const LOGO_BELIRME = 0.28;
|
||||
const LOGO_DURUS = 0.16;
|
||||
const LOGO_SONME = 0.3;
|
||||
const LOGO_SURE = LOGO_BELIRME + LOGO_DURUS + LOGO_SONME;
|
||||
const LOGO_ZAMANLARI = [
|
||||
0,
|
||||
(KARE_SURE * KARE_ZAMANLARI[1]) / LOGO_SURE,
|
||||
(KARE_SURE * KARE_ZAMANLARI[2]) / LOGO_SURE,
|
||||
LOGO_BELIRME / LOGO_SURE,
|
||||
(LOGO_BELIRME + LOGO_DURUS) / LOGO_SURE,
|
||||
1,
|
||||
];
|
||||
const KARTSIZ_SLOT_ADIMI = 0.05;
|
||||
const KARTSIZ_ICERIK_BASI = 0.6;
|
||||
const KARTSIZ_BLOK_ADIMI = 0.05;
|
||||
const KARTSIZ_NETLESME = 0.35;
|
||||
|
||||
/** Kartsız modun logo kareleri, belirme sırasıyla (`data-logo-kare` değerleri). */
|
||||
export const LOGO_SLOTLARI = SLOT_SIRASI;
|
||||
@@ -80,13 +94,16 @@ export function logoKareStili(slot: Slot, kapBoyu = 64) {
|
||||
* Kareler yalnız transform ile oynar (yerleşim ölçülür, bozulmaz); içerik o
|
||||
* sırada görünmez olduğu için kare ölçeklenirken yazı ezilmez.
|
||||
*
|
||||
* `hazir`: false iken sahne oynamaz (iskelet görünür kalır), true olunca
|
||||
* oynar — veri bekleyen çekmece için. Varsayılan true (içerik sunucudan
|
||||
* hazır gelen çekmece).
|
||||
* `hazir`: veri bekleyen çekmece için. false iken sahne oynamaz (iskelet
|
||||
* görünür kalır); sahne yalnız false → true geçişinde oynar. Bileşen
|
||||
* `hazir={true}` ile monte olursa veri önbellektedir (çekmece yeniden
|
||||
* açıldı): sahne atlanır, içerik 0,25 s solmayla gelir — kareler o sırada
|
||||
* çekmece daha ekrana kaymadan oynuyor, gövde boş kalıyordu. Verilmezse
|
||||
* (içerik sunucudan hazır gelen çekmece) sahne her montajda oynar.
|
||||
*/
|
||||
export function AcilisSahnesi({
|
||||
children,
|
||||
hazir = true,
|
||||
hazir,
|
||||
className = "mt-5",
|
||||
}: {
|
||||
children: ReactNode;
|
||||
@@ -95,12 +112,16 @@ export function AcilisSahnesi({
|
||||
}) {
|
||||
const kapRef = useRef<HTMLDivElement>(null);
|
||||
const azHareket = useReducedMotion();
|
||||
// Montajda veri hazır mıydı? (ref: Strict Mode'un çift effect'inde de
|
||||
// aynı kalır; yeniden montajda — çekmece yeniden açılınca — yeniden ölçülür)
|
||||
const onbellekli = useRef(hazir === true);
|
||||
const oynat = hazir ?? true;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const kap = kapRef.current;
|
||||
// hazir false → true tek geçiş: sahne o geçişte bir kez oynar (ref
|
||||
// tutulmaz; Strict Mode'un çift effect'i temizlik sonrası yeniden kurar).
|
||||
if (!kap || !hazir) return;
|
||||
if (!kap || !oynat) return;
|
||||
const sahne = kap.querySelector<HTMLElement>("[data-sahne]");
|
||||
const logo = kap.querySelector<HTMLElement>("[data-logo]");
|
||||
const kareler = Array.from(
|
||||
@@ -117,8 +138,10 @@ export function AcilisSahnesi({
|
||||
const kartli = !!sahne && kareler.length > 0;
|
||||
const kartsiz = !kartli && !!logo && logoKareleri.length > 0;
|
||||
|
||||
if (azHareket || (!kartli && !kartsiz)) {
|
||||
// Logo kabı sahnesiz anlamsız: yer kaplamasın.
|
||||
if (azHareket || onbellekli.current || (!kartli && !kartsiz)) {
|
||||
// Sahnesiz yol (hareketi azalt, önbellekli açılış): içerik solarak
|
||||
// gelir, gövde hiçbir an boş kalmaz. Logo kabı sahnesiz anlamsız: yer
|
||||
// kaplamasın.
|
||||
if (logo) {
|
||||
logo.style.display = "none";
|
||||
dokunulan.push(logo);
|
||||
@@ -133,7 +156,7 @@ export function AcilisSahnesi({
|
||||
}
|
||||
|
||||
// Bulanıklıktan netleşerek, hafifçe aşağıdan gelen satır.
|
||||
const netlestir = (el: HTMLElement, gecikme: number) => {
|
||||
const netlestir = (el: HTMLElement, gecikme: number, sure = 0.5) => {
|
||||
el.style.opacity = "0";
|
||||
dokunulan.push(el);
|
||||
const k = animate(
|
||||
@@ -143,7 +166,7 @@ export function AcilisSahnesi({
|
||||
filter: ["blur(10px)", "blur(0px)"],
|
||||
y: [10, 0],
|
||||
},
|
||||
{ duration: 0.5, delay: gecikme, ease: GUCLU_CIKIS },
|
||||
{ duration: sure, delay: gecikme, ease: GUCLU_CIKIS },
|
||||
);
|
||||
k.then(() => {
|
||||
el.style.filter = "";
|
||||
@@ -168,11 +191,12 @@ export function AcilisSahnesi({
|
||||
|
||||
if (kartsiz) {
|
||||
// Kartsız: logo kareleri belirir, durur, söner; kap sönerken sıfır boya
|
||||
// iner ki altında boşluk kalmasın; içerik ICERIK_BASI'ndan itibaren gelir.
|
||||
// iner ki altında boşluk kalmasın; içerik KARTSIZ_ICERIK_BASI'ndan
|
||||
// itibaren, sönmeyle çaprazlanarak gelir.
|
||||
let sonKare = 0;
|
||||
logoKareleri.forEach((el) => {
|
||||
const slot = (el.dataset.logoKare ?? "sol") as Slot;
|
||||
const gecikme = BASLANGIC + SLOT_SIRASI.indexOf(slot) * SLOT_ADIMI;
|
||||
const gecikme = BASLANGIC + SLOT_SIRASI.indexOf(slot) * KARTSIZ_SLOT_ADIMI;
|
||||
sonKare = Math.max(sonKare, gecikme);
|
||||
el.style.opacity = "0";
|
||||
dokunulan.push(el);
|
||||
@@ -197,14 +221,16 @@ export function AcilisSahnesi({
|
||||
const kapat = animate(
|
||||
logo!,
|
||||
{ height: [logo!.offsetHeight, 0] },
|
||||
{ duration: 0.4, delay: sonmeBasi, ease: YERLESME },
|
||||
{ duration: LOGO_SONME, delay: sonmeBasi, ease: YERLESME },
|
||||
);
|
||||
kapat.then(() => {
|
||||
logo!.style.display = "none";
|
||||
});
|
||||
kontroller.push(kapat);
|
||||
|
||||
sonra.forEach((el, i) => netlestir(el, ICERIK_BASI + i * 0.08));
|
||||
sonra.forEach((el, i) =>
|
||||
netlestir(el, KARTSIZ_ICERIK_BASI + i * KARTSIZ_BLOK_ADIMI, KARTSIZ_NETLESME),
|
||||
);
|
||||
return temizle;
|
||||
}
|
||||
|
||||
@@ -298,7 +324,7 @@ export function AcilisSahnesi({
|
||||
);
|
||||
|
||||
return temizle;
|
||||
}, [azHareket, hazir]);
|
||||
}, [azHareket, oynat]);
|
||||
|
||||
return (
|
||||
<div ref={kapRef} className={className}>
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
// türleri, son yerleşenin netleri, yerleşme koşulları, TÜİK mezun istihdamı,
|
||||
// üniversite özeti. API'nin `gecmis` (yıllara göre sıra/puan/kontenjan)
|
||||
// alanı burada gösterilmez (Bilal, 7 Eki 2026: yıl tablosu kaldırıldı;
|
||||
// backlog). Açılış: veri gelince logo kareleri belirir, söner, içerik
|
||||
// bloklar hâlinde netleşir (AcilisSahnesi kartsız modu).
|
||||
// backlog). Açılış: veri ilk gelince logo kareleri belirir, söner, içerik
|
||||
// bloklar hâlinde netleşir (AcilisSahnesi kartsız modu); veri önbellekteyken
|
||||
// (çekmece yeniden açıldı) sahne yok, içerik 0,25 s solmayla gelir.
|
||||
// Kadın-erkek dağılımı program düzeyinde bilinçli olarak yok: YÖK Atlas 2026
|
||||
// SPA'sına geçerken cinsiyet panelini yayından kaldırdı (bkz. scripts/detay.ts).
|
||||
// Çekmece içerik ağırlıklı olduğu için liste çekmecesi gibi kaydırılır (§4.10
|
||||
@@ -226,7 +227,7 @@ function AyrintiIcerik({ durum, veri }: { durum: Durum; veri: ProgramAyrintisi |
|
||||
);
|
||||
}
|
||||
// Sıra = AcilisSahnesi'nin `data-sonra` sırası: logo söner, bloklar
|
||||
// yukarıdan aşağıya 0,08 s arayla netleşir.
|
||||
// yukarıdan aşağıya 0,05 s arayla netleşir.
|
||||
return (
|
||||
<>
|
||||
{/* Logo kareleri: sahne sırasında görünür, sönünce kap sıfır boya iner. */}
|
||||
@@ -473,12 +474,15 @@ function Istihdam({ veri }: { veri: ProgramAyrintisi }) {
|
||||
v.kazanc ? `kazanç grubu ${v.kazanc.toLocaleLowerCase("tr-TR")}` : null,
|
||||
].filter(Boolean);
|
||||
if (parcalar.length === 0) return null;
|
||||
// Cümle başı büyük (§10.4); hangi parçanın başa geldiği veriye bağlı.
|
||||
const birlesik = parcalar.join(", ");
|
||||
const cumle = birlesik.charAt(0).toLocaleUpperCase("tr-TR") + birlesik.slice(1);
|
||||
const ort = v.ortalama.istihdam != null ? ` Türkiye ortalaması %${ondalik(v.ortalama.istihdam)}.` : "";
|
||||
return (
|
||||
<div data-sonra>
|
||||
<h3 className={BOLUM_BASLIK}>Mezun istihdamı</h3>
|
||||
<p className={NOT}>
|
||||
{parcalar.join(", ")} (TÜİK {v.yil}, ülke geneli, {v.duzey === "lisans" ? "lisans" : "ön lisans"}{" "}
|
||||
{cumle} (TÜİK {v.yil}, ülke geneli, {v.duzey === "lisans" ? "lisans" : "ön lisans"}{" "}
|
||||
mezunları; üniversiteye göre değişir, kırılım yok).{ort}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user