165 lines
6.6 KiB
TypeScript
Executable File
165 lines
6.6 KiB
TypeScript
Executable File
|
||
import React, { useState, useEffect, useRef } from 'react';
|
||
import Hero from './components/Hero';
|
||
import Infrastructure from './components/Infrastructure';
|
||
import Science from './components/Science';
|
||
import Social from './components/Social';
|
||
import Leadership from './components/Leadership';
|
||
import Metrics from './components/Metrics';
|
||
import Scaling from './components/Scaling';
|
||
import Team from './components/Team';
|
||
import Timeline from './components/Timeline';
|
||
import BootAnimation from './components/BootAnimation';
|
||
import Modal from './components/Modal';
|
||
import { ModalData } from './types';
|
||
// import TechVisualizer from './components/TechVisualizer'; // Disabled per user request
|
||
import { ArrowUp } from 'lucide-react';
|
||
import { motion, AnimatePresence } from 'framer-motion';
|
||
|
||
const App: React.FC = () => {
|
||
const [isBooting, setIsBooting] = useState(true);
|
||
const [showScrollTop, setShowScrollTop] = useState(false);
|
||
const [modalData, setModalData] = useState<ModalData | null>(null);
|
||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||
|
||
// Theme and Scroll Logic
|
||
useEffect(() => {
|
||
const container = scrollContainerRef.current;
|
||
if (!container) return;
|
||
|
||
const handleScroll = () => {
|
||
const scrollY = container.scrollTop;
|
||
const windowHeight = window.innerHeight;
|
||
|
||
const leadershipSection = document.getElementById('leadership'); // Block 4
|
||
const teamSection = document.getElementById('team');
|
||
|
||
// --- Theme Logic: Light mode starting from Leadership (Block 4) ---
|
||
if (leadershipSection && teamSection) {
|
||
// We use offsetTop relative to the container usually, but since sections are stacked
|
||
// and container scrolls, standard offsetTop works if container is relative/static.
|
||
const leadershipTrigger = leadershipSection.offsetTop - windowHeight * 0.6;
|
||
const teamTrigger = teamSection.offsetTop - windowHeight * 0.8;
|
||
|
||
// Active Light Mode for Leadership, Metrics, Scaling. Back to Dark for Team.
|
||
if (scrollY >= leadershipTrigger && scrollY < teamTrigger) {
|
||
document.body.classList.add('theme-light');
|
||
} else {
|
||
document.body.classList.remove('theme-light');
|
||
}
|
||
}
|
||
|
||
if (scrollY > 300) setShowScrollTop(true);
|
||
else setShowScrollTop(false);
|
||
};
|
||
|
||
container.addEventListener('scroll', handleScroll);
|
||
handleScroll(); // Trigger once on load
|
||
|
||
return () => {
|
||
container.removeEventListener('scroll', handleScroll);
|
||
document.body.classList.remove('theme-light');
|
||
};
|
||
}, [isBooting]);
|
||
|
||
const scrollToTop = () => {
|
||
if (scrollContainerRef.current) {
|
||
scrollContainerRef.current.scrollTo({ top: 0, behavior: 'smooth' });
|
||
}
|
||
};
|
||
|
||
const handleOpenModal = (data: ModalData) => {
|
||
setModalData(data);
|
||
};
|
||
|
||
const handleCloseModal = () => {
|
||
setModalData(null);
|
||
};
|
||
|
||
const handleContactClick = () => {
|
||
handleOpenModal({
|
||
title: 'Связь с разработчиком',
|
||
type: 'form',
|
||
theme: 'dark', // Keep header interaction dark themed typically
|
||
content: {
|
||
text: 'Оставьте свои контакты, и мы свяжемся с вами для обсуждения интеграции.'
|
||
}
|
||
});
|
||
};
|
||
|
||
return (
|
||
<div className="h-screen w-full bg-theme-main overflow-hidden relative">
|
||
<AnimatePresence>
|
||
{isBooting && <BootAnimation onComplete={() => setIsBooting(false)} />}
|
||
</AnimatePresence>
|
||
|
||
{!isBooting && (
|
||
<div className="relative h-full w-full">
|
||
{/* Grid Pattern Background */}
|
||
<div className="absolute inset-0 pointer-events-none grid-pattern z-0" />
|
||
|
||
{/* Header */}
|
||
<nav className="fixed top-0 left-0 right-0 z-[90] py-6 mix-blend-difference text-white pointer-events-none">
|
||
<div className="max-w-7xl mx-auto px-6 flex justify-between items-center pointer-events-auto">
|
||
<div className="font-mono text-sm font-bold tracking-widest flex items-center gap-3">
|
||
{/* Logo Icon */}
|
||
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg" className="w-12 h-12">
|
||
<circle cx="100" cy="100" r="70" stroke="#ff4b4b" strokeWidth="6" fill="none" strokeDasharray="15 85"/>
|
||
<circle cx="100" cy="100" r="50" stroke="#ff4b4b" strokeWidth="6" fill="none" strokeDasharray="12 58" transform="rotate(30 100 100)"/>
|
||
<circle cx="100" cy="100" r="30" stroke="#ff4b4b" strokeWidth="6" fill="none" strokeDasharray="8 32" transform="rotate(60 100 100)"/>
|
||
</svg>
|
||
iiEasy <span className="opacity-50"> // Будущее.Просто.</span>
|
||
</div>
|
||
|
||
<button
|
||
onClick={handleContactClick}
|
||
className="text-xs font-mono font-bold hover:text-[#20e3b2] transition-colors uppercase cursor-pointer"
|
||
>
|
||
[ Связь ]
|
||
</button>
|
||
</div>
|
||
</nav>
|
||
|
||
<Timeline />
|
||
|
||
{/* Scroll Container */}
|
||
<main
|
||
ref={scrollContainerRef}
|
||
className="h-full w-full overflow-y-scroll snap-y snap-proximity lg:snap-mandatory snap-container scroll-smooth relative z-10"
|
||
>
|
||
<Hero id="hero" onOpenModal={handleOpenModal} />
|
||
<Infrastructure onOpenModal={handleOpenModal} />
|
||
<Science onOpenModal={handleOpenModal} />
|
||
<Social onOpenModal={handleOpenModal} />
|
||
<Leadership onOpenModal={handleOpenModal} />
|
||
<Metrics onOpenModal={handleOpenModal} />
|
||
<Scaling onOpenModal={handleOpenModal} />
|
||
<Team onOpenModal={handleOpenModal} />
|
||
</main>
|
||
|
||
{/* Global Modal */}
|
||
<AnimatePresence>
|
||
{modalData && <Modal data={modalData} onClose={handleCloseModal} />}
|
||
</AnimatePresence>
|
||
</div>
|
||
)}
|
||
|
||
<AnimatePresence>
|
||
{showScrollTop && !isBooting && !modalData && (
|
||
<motion.button
|
||
initial={{ opacity: 0, y: 20 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
exit={{ opacity: 0, y: 20 }}
|
||
onClick={scrollToTop}
|
||
className="fixed bottom-8 right-8 z-50 p-4 bg-theme-main border border-theme text-theme-main hover:border-[#ff4b4b] transition-colors"
|
||
>
|
||
<ArrowUp className="w-5 h-5" />
|
||
</motion.button>
|
||
)}
|
||
</AnimatePresence>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default App;
|