mitose e meiose

Signed-off-by: Rodrigo Bertoldo de Sousa Martins <rodrigo@martinsevieira.adv.br>
This commit is contained in:
Rodrigo Bertoldo de Sousa Martins committed 2026-09-17 12:29:49 -03:00
1 parent f32833c023
commit 734b967f11
4 files changed
+366 -13

No files matched your search

+129 -12
View File
@@ -1,32 +1,97 @@
import { useEffect, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { Canvas } from "@react-three/fiber";
import { CameraControls } from "@react-three/drei";
import { CellDivisionScene } from "../components/cellDivision/CellDivisionScene.js";
import { MITOSIS_STAGES, MEIOSIS_STAGES } from "../data/cellDivisionStages.js";
import {
MITOSIS_STAGES,
MEIOSIS_STAGES,
MEIOSIS_I_ERROR_STAGES,
MEIOSIS_II_ERROR_STAGES,
} from "../data/cellDivisionStages.js";
import { GENETIC_SYNDROMES, ERROR_MECHANISM_LABELS, type ErrorMechanism } from "../data/geneticSyndromes.js";
type Mode = "mitose" | "meiose";
type Mode = "mitose" | "meiose" | "erros";
type ErrorType = "meiose1" | "meiose2";
const AUTOPLAY_MS = 3200;
function SyndromeCard({ mechanism, compact }: { mechanism?: ErrorMechanism; compact?: boolean }) {
const list = mechanism ? GENETIC_SYNDROMES.filter((s) => s.mechanism === mechanism) : GENETIC_SYNDROMES;
return (
<div className="space-y-3">
{list.map((s) => (
<div key={s.id} className={compact ? "" : "border border-white/10 rounded-lg p-3 bg-white/[0.03]"}>
<div className="flex items-baseline justify-between gap-2 flex-wrap">
<h4 className="text-sm font-semibold text-white">{s.namePt}</h4>
<span className="text-[11px] text-slate-500">{s.karyotype}</span>
</div>
{!compact && (
<span className="inline-block text-[10px] px-1.5 py-0.5 rounded bg-accent/15 text-accent mt-1">
{ERROR_MECHANISM_LABELS[s.mechanism]}
</span>
)}
<p className="text-xs text-slate-400 mt-1.5 leading-relaxed">{s.originNote}</p>
<p className="text-xs text-slate-300 mt-1.5 leading-relaxed">{s.description}</p>
</div>
))}
</div>
);
}
function SyndromeModal({ onClose }: { onClose: () => void }) {
return (
<div className="fixed inset-0 z-40 bg-ink/80 flex items-center justify-center p-4" onClick={onClose}>
<div
className="bg-panel border border-white/10 rounded-xl shadow-2xl p-4 max-w-2xl w-full max-h-[85vh] overflow-y-auto"
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center justify-between mb-3">
<h2 className="text-sm font-semibold text-white">Síndromes genéticas — visão geral</h2>
<button onClick={onClose} className="text-slate-400 hover:text-white text-sm px-2 py-1" aria-label="Fechar">
✕
</button>
</div>
<p className="text-xs text-slate-400 mb-3 leading-relaxed">
A maioria vem de <strong className="text-slate-300">não-disjunção</strong> (um par de cromossomos que não se separa — ver as animações desta página); a última é um exemplo de{" "}
<strong className="text-slate-300">aberração estrutural</strong> (perda de um pedaço do cromossomo), um mecanismo diferente.
</p>
<SyndromeCard />
</div>
</div>
);
}
export default function CellDivisionPage() {
const [mode, setMode] = useState<Mode>("mitose");
const [errorType, setErrorType] = useState<ErrorType>("meiose1");
const [stageIndex, setStageIndex] = useState(0);
const [playing, setPlaying] = useState(false);
const [leftOpen, setLeftOpen] = useState(false);
const [rightOpen, setRightOpen] = useState(false);
const [showSyndromeModal, setShowSyndromeModal] = useState(false);
const stages = mode === "mitose" ? MITOSIS_STAGES : MEIOSIS_STAGES;
const stages = useMemo(() => {
if (mode === "mitose") return MITOSIS_STAGES;
if (mode === "meiose") return MEIOSIS_STAGES;
return errorType === "meiose1" ? MEIOSIS_I_ERROR_STAGES : MEIOSIS_II_ERROR_STAGES;
}, [mode, errorType]);
const stage = stages[stageIndex];
// Trocar de modo reinicia o estágio — misturar índice de mitose (7
// estágios) com o array de meiose (9 estágios) fora de ordem não faz
// sentido pedagogicamente.
// Trocar de modo (ou, dentro de "erros", de tipo de não-disjunção)
// reinicia o estágio — misturar o índice de uma sequência com o array
// de outra não faz sentido pedagogicamente.
function changeMode(next: Mode) {
setMode(next);
setStageIndex(0);
setPlaying(false);
}
function changeErrorType(next: ErrorType) {
setErrorType(next);
setStageIndex(0);
setPlaying(false);
}
function goTo(index: number) {
setStageIndex(Math.max(0, Math.min(stages.length - 1, index)));
}
@@ -87,13 +152,41 @@ export default function CellDivisionPage() {
Meiose
</button>
</div>
<button
onClick={() => changeMode("erros")}
className={`w-full mt-1.5 text-sm py-2 rounded-lg border ${
mode === "erros" ? "bg-red-500/20 border-red-500/40 text-red-300" : "border-white/10 bg-white/5 text-slate-300 hover:bg-white/10"
}`}
>
⚠ Erros e Síndromes
</button>
<p className="text-[11px] text-slate-500 mt-2 leading-relaxed">
{mode === "mitose"
? "1 célula (2n=4, simplificado) → 2 células-filhas idênticas."
: "1 célula (2n=4) → 4 células-filhas haploides, geneticamente diferentes entre si."}
{mode === "mitose" && "1 célula (2n=4, simplificado) → 2 células-filhas idênticas."}
{mode === "meiose" && "1 célula (2n=4) → 4 células-filhas haploides, geneticamente diferentes entre si."}
{mode === "erros" && "O que acontece quando um par de cromossomos deixa de se separar — a origem da maioria das síndromes de aneuploidia."}
</p>
</div>
{mode === "erros" && (
<div>
<h3 className="text-xs uppercase tracking-wide text-slate-400 mb-2">Onde o erro ocorre</h3>
<div className="flex rounded-lg overflow-hidden border border-white/10">
<button
onClick={() => changeErrorType("meiose1")}
className={`flex-1 text-sm py-2 ${errorType === "meiose1" ? "bg-accent text-white" : "bg-white/5 text-slate-300 hover:bg-white/10"}`}
>
Meiose I
</button>
<button
onClick={() => changeErrorType("meiose2")}
className={`flex-1 text-sm py-2 ${errorType === "meiose2" ? "bg-accent text-white" : "bg-white/5 text-slate-300 hover:bg-white/10"}`}
>
Meiose II
</button>
</div>
</div>
)}
<div>
<h3 className="text-xs uppercase tracking-wide text-slate-400 mb-2">Fases</h3>
<ol className="space-y-1">
@@ -121,6 +214,15 @@ export default function CellDivisionPage() {
))}
</ol>
</div>
{mode === "erros" && (
<button
onClick={() => setShowSyndromeModal(true)}
className="w-full text-sm py-2 rounded-lg border border-white/10 bg-white/5 text-slate-300 hover:bg-white/10"
>
📋 Ver todas as síndromes
</button>
)}
</aside>
{(leftOpen || rightOpen) && (
@@ -206,12 +308,25 @@ export default function CellDivisionPage() {
>
✕ Fechar
</button>
<span className="inline-block text-xs px-2 py-1 rounded-full bg-accent/20 text-accent">
{mode === "mitose" ? "Mitose" : "Meiose"}
<span
className={`inline-block text-xs px-2 py-1 rounded-full ${
mode === "erros" ? "bg-red-500/20 text-red-300" : "bg-accent/20 text-accent"
}`}
>
{mode === "mitose" && "Mitose"}
{mode === "meiose" && "Meiose"}
{mode === "erros" && `Erro na ${ERROR_MECHANISM_LABELS[errorType]}`}
</span>
<h2 className="text-lg font-semibold text-white leading-snug">{stage.label}</h2>
<p className="text-sm text-slate-300 leading-relaxed">{stage.description}</p>
{mode === "erros" && (
<div className="pt-3 border-t border-white/10 space-y-2">
<p className="text-[11px] uppercase tracking-wide text-slate-500">Síndromes ligadas a este erro</p>
<SyndromeCard mechanism={errorType} compact />
</div>
)}
<div className="pt-3 border-t border-white/10 space-y-1.5">
<p className="text-[11px] uppercase tracking-wide text-slate-500">Legenda das cores</p>
<div className="flex items-center gap-2 text-xs text-slate-400">
@@ -236,6 +351,8 @@ export default function CellDivisionPage() {
Modelo ilustrativo e simplificado (2n=4 em vez dos 46 cromossomos humanos reais) — pensado pra mostrar os conceitos-chave de cada fase, não uma simulação biofísica exata. Arraste pra girar, role pra dar zoom.
</p>
</aside>
{showSyndromeModal && <SyndromeModal onClose={() => setShowSyndromeModal(false)} />}
</div>
);
}