v2 #10
@@ -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`)
|
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
|
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,
|
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
|
kaynak) 0,05 s arayla bulanıktan netleşir; toplam ≈ 1,2 s (kareler 0,1–0,2 s'de belirir,
|
||||||
solma (§6). Akademik göstergeler çekmecesi (§4.9) aynı bileşenin kartlı modunu kullanır.
|
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`
|
### 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
|
- **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
|
// `[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
|
// sönerek kaybolur (scale 0.6 + opacity 0; Bilal, 7 Eki 2026) ve
|
||||||
// `[data-sonra]` bloklar netleşerek gelir.
|
// `[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 { useLayoutEffect, useRef, type ReactNode } from "react";
|
||||||
import { animate, useReducedMotion } from "motion/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 GUCLU_CIKIS = [0.23, 1, 0.32, 1] as const;
|
||||||
const YERLESME = [0.32, 0.72, 0, 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ı
|
// Kartsız mod (saniye): logo karesi belir → dur → sön; kareler 0,05 s arayla
|
||||||
// modla aynı anlara denk gelir (KARE_SURE·0.24, ·0.52); sönme 0,4 s.
|
// gelir, içerik blokları sönmeyle çaprazlanarak 0,05 s arayla netleşir.
|
||||||
const LOGO_SURE = KARE_SURE * KARE_ZAMANLARI[2] + 0.4;
|
// 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 = [
|
const LOGO_ZAMANLARI = [
|
||||||
0,
|
0,
|
||||||
(KARE_SURE * KARE_ZAMANLARI[1]) / LOGO_SURE,
|
LOGO_BELIRME / LOGO_SURE,
|
||||||
(KARE_SURE * KARE_ZAMANLARI[2]) / LOGO_SURE,
|
(LOGO_BELIRME + LOGO_DURUS) / LOGO_SURE,
|
||||||
1,
|
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). */
|
/** Kartsız modun logo kareleri, belirme sırasıyla (`data-logo-kare` değerleri). */
|
||||||
export const LOGO_SLOTLARI = SLOT_SIRASI;
|
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
|
* 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.
|
* 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
|
* `hazir`: veri bekleyen çekmece için. false iken sahne oynamaz (iskelet
|
||||||
* oynar — veri bekleyen çekmece için. Varsayılan true (içerik sunucudan
|
* görünür kalır); sahne yalnız false → true geçişinde oynar. Bileşen
|
||||||
* hazır gelen çekmece).
|
* `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({
|
export function AcilisSahnesi({
|
||||||
children,
|
children,
|
||||||
hazir = true,
|
hazir,
|
||||||
className = "mt-5",
|
className = "mt-5",
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -95,12 +112,16 @@ export function AcilisSahnesi({
|
|||||||
}) {
|
}) {
|
||||||
const kapRef = useRef<HTMLDivElement>(null);
|
const kapRef = useRef<HTMLDivElement>(null);
|
||||||
const azHareket = useReducedMotion();
|
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(() => {
|
useLayoutEffect(() => {
|
||||||
const kap = kapRef.current;
|
const kap = kapRef.current;
|
||||||
// hazir false → true tek geçiş: sahne o geçişte bir kez oynar (ref
|
// 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).
|
// 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 sahne = kap.querySelector<HTMLElement>("[data-sahne]");
|
||||||
const logo = kap.querySelector<HTMLElement>("[data-logo]");
|
const logo = kap.querySelector<HTMLElement>("[data-logo]");
|
||||||
const kareler = Array.from(
|
const kareler = Array.from(
|
||||||
@@ -117,8 +138,10 @@ export function AcilisSahnesi({
|
|||||||
const kartli = !!sahne && kareler.length > 0;
|
const kartli = !!sahne && kareler.length > 0;
|
||||||
const kartsiz = !kartli && !!logo && logoKareleri.length > 0;
|
const kartsiz = !kartli && !!logo && logoKareleri.length > 0;
|
||||||
|
|
||||||
if (azHareket || (!kartli && !kartsiz)) {
|
if (azHareket || onbellekli.current || (!kartli && !kartsiz)) {
|
||||||
// Logo kabı sahnesiz anlamsız: yer kaplamasın.
|
// 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) {
|
if (logo) {
|
||||||
logo.style.display = "none";
|
logo.style.display = "none";
|
||||||
dokunulan.push(logo);
|
dokunulan.push(logo);
|
||||||
@@ -133,7 +156,7 @@ export function AcilisSahnesi({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bulanıklıktan netleşerek, hafifçe aşağıdan gelen satır.
|
// 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";
|
el.style.opacity = "0";
|
||||||
dokunulan.push(el);
|
dokunulan.push(el);
|
||||||
const k = animate(
|
const k = animate(
|
||||||
@@ -143,7 +166,7 @@ export function AcilisSahnesi({
|
|||||||
filter: ["blur(10px)", "blur(0px)"],
|
filter: ["blur(10px)", "blur(0px)"],
|
||||||
y: [10, 0],
|
y: [10, 0],
|
||||||
},
|
},
|
||||||
{ duration: 0.5, delay: gecikme, ease: GUCLU_CIKIS },
|
{ duration: sure, delay: gecikme, ease: GUCLU_CIKIS },
|
||||||
);
|
);
|
||||||
k.then(() => {
|
k.then(() => {
|
||||||
el.style.filter = "";
|
el.style.filter = "";
|
||||||
@@ -168,11 +191,12 @@ export function AcilisSahnesi({
|
|||||||
|
|
||||||
if (kartsiz) {
|
if (kartsiz) {
|
||||||
// Kartsız: logo kareleri belirir, durur, söner; kap sönerken sıfır boya
|
// 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;
|
let sonKare = 0;
|
||||||
logoKareleri.forEach((el) => {
|
logoKareleri.forEach((el) => {
|
||||||
const slot = (el.dataset.logoKare ?? "sol") as Slot;
|
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);
|
sonKare = Math.max(sonKare, gecikme);
|
||||||
el.style.opacity = "0";
|
el.style.opacity = "0";
|
||||||
dokunulan.push(el);
|
dokunulan.push(el);
|
||||||
@@ -197,14 +221,16 @@ export function AcilisSahnesi({
|
|||||||
const kapat = animate(
|
const kapat = animate(
|
||||||
logo!,
|
logo!,
|
||||||
{ height: [logo!.offsetHeight, 0] },
|
{ height: [logo!.offsetHeight, 0] },
|
||||||
{ duration: 0.4, delay: sonmeBasi, ease: YERLESME },
|
{ duration: LOGO_SONME, delay: sonmeBasi, ease: YERLESME },
|
||||||
);
|
);
|
||||||
kapat.then(() => {
|
kapat.then(() => {
|
||||||
logo!.style.display = "none";
|
logo!.style.display = "none";
|
||||||
});
|
});
|
||||||
kontroller.push(kapat);
|
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;
|
return temizle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +324,7 @@ export function AcilisSahnesi({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return temizle;
|
return temizle;
|
||||||
}, [azHareket, hazir]);
|
}, [azHareket, oynat]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={kapRef} className={className}>
|
<div ref={kapRef} className={className}>
|
||||||
|
|||||||
@@ -7,8 +7,9 @@
|
|||||||
// türleri, son yerleşenin netleri, yerleşme koşulları, TÜİK mezun istihdamı,
|
// 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)
|
// ü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ı;
|
// 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
|
// backlog). Açılış: veri ilk gelince logo kareleri belirir, söner, içerik
|
||||||
// bloklar hâlinde netleşir (AcilisSahnesi kartsız modu).
|
// 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
|
// 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).
|
// 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
|
// Ç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
|
// 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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Logo kareleri: sahne sırasında görünür, sönünce kap sıfır boya iner. */}
|
{/* 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,
|
v.kazanc ? `kazanç grubu ${v.kazanc.toLocaleLowerCase("tr-TR")}` : null,
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
if (parcalar.length === 0) return null;
|
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)}.` : "";
|
const ort = v.ortalama.istihdam != null ? ` Türkiye ortalaması %${ondalik(v.ortalama.istihdam)}.` : "";
|
||||||
return (
|
return (
|
||||||
<div data-sonra>
|
<div data-sonra>
|
||||||
<h3 className={BOLUM_BASLIK}>Mezun istihdamı</h3>
|
<h3 className={BOLUM_BASLIK}>Mezun istihdamı</h3>
|
||||||
<p className={NOT}>
|
<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}
|
mezunları; üniversiteye göre değişir, kırılım yok).{ort}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user