refactor(hareket): sihirbaz adım geçişi sabitleri lib/adim-gecisi.ts'e; soru formu aynı geçişle

- adimGecisiVaryantlari / adimGecisi tek kaynak (spring 300/30, opaklık 0,2 s;
  hareketi azalt'ta 0,12 s çapraz solma). Sihirbaz davranışı aynı; center
  varyantına `position: relative` eklendi ki geri dönen panel mutlak kalmasın.
- SohbetAnketi kontrollü (item/onItemChange); her soru motion.div içinde,
  pasif sorular hidden={false} + mutlak/saydam park eder (ilkelin inert'i
  kalır), aktif fieldset useMeasure ile ölçülüp boy yayla uzar. Çıkış animasyonu
  da çalışır: mergeProps'ta kullanıcı prop'u kazanıyor.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-10-07 23:14:34 +03:00
parent f08a26a022
commit 3f4095e615
3 changed files with 148 additions and 68 deletions

65
src/lib/adim-gecisi.ts Normal file
View File

@@ -0,0 +1,65 @@
// Adım adım ilerleyen panellerin ortak geçişi (DESIGN.md §6): sihirbazın
// alan → il → öncelik adımları (sihirbaz-adimlar.tsx, TransitionPanel) ile
// danışmanın soru formundaki sorular (sohbet-anketi.tsx) aynı yoldan gelir
// gider — yeni adım ileri giderken sağdan, geri dönerken soldan kayar; boy
// ölçülen yüksekliğe yayla uzar. Sabitler yalnız burada; iki yerde de bu iki
// fonksiyon çağrılır, kopyası yazılmaz. Yay sekmesiz (stiffness 300 /
// damping 30), opaklık 0,2 s. "Hareketi azalt"ta kayma ve boy yayı yok,
// yalnız 0,12 s'lik çapraz solma.
import type { Transition, Variants } from "motion/react";
/**
* enter / center / exit varyantları; `custom` geçiş yönüdür (1 ileri, -1
* geri). Giren panel akışta (`relative`) durur, çıkan panel mutlak konuma
* alınır ki ikisi aynı anda üst üste kayabilsin. `yukseklik` ölçülen boy
* (ilk ölçümden önce "auto").
*/
export function adimGecisiVaryantlari({
hareketiAzalt,
yukseklik,
}: {
hareketiAzalt: boolean;
yukseklik: number | "auto";
}): Variants {
return {
enter: (gecisYonu: number) => ({
x: hareketiAzalt ? 0 : gecisYonu > 0 ? "100%" : "-100%",
opacity: 0,
height: yukseklik,
position: "relative",
}),
center: {
zIndex: 1,
x: 0,
opacity: 1,
height: yukseklik,
position: "relative",
},
exit: (gecisYonu: number) => ({
zIndex: 0,
x: hareketiAzalt ? 0 : gecisYonu < 0 ? "100%" : "-100%",
opacity: 0,
position: "absolute",
top: 0,
left: 0,
width: "100%",
}),
};
}
/** Geçişin zamanlaması; azaltılmış harekette x/height açıkça sıfırlanır ki
* varsayılan yaya düşüp oynamasın. */
export function adimGecisi(hareketiAzalt: boolean): Transition {
return hareketiAzalt
? {
opacity: { duration: 0.12 },
x: { duration: 0 },
height: { duration: 0 },
}
: {
x: { type: "spring", stiffness: 300, damping: 30 },
height: { type: "spring", stiffness: 300, damping: 30 },
opacity: { duration: 0.2 },
};
}