Neue services
This commit is contained in:
177
App.tsx
177
App.tsx
@@ -1,65 +1,93 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route, useLocation } from 'react-router-dom';
|
||||
import Lenis from '@studio-freight/lenis';
|
||||
import React, { Suspense, useEffect, useRef } from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route, Navigate, useLocation } from 'react-router-dom';
|
||||
import gsap from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||
import { ScrollToPlugin } from 'gsap/ScrollToPlugin';
|
||||
import Navbar from './components/Navbar';
|
||||
import Footer from './components/Footer';
|
||||
import BackToTop from './components/BackToTop';
|
||||
import HomePage from './src/pages/HomePage';
|
||||
import AboutPage from './src/pages/AboutPage';
|
||||
import ServicesPage from './src/pages/ServicesPage';
|
||||
import BlogPage from './src/pages/BlogPage';
|
||||
import ContactPage from './src/pages/ContactPage';
|
||||
import LocationPage from './src/pages/LocationPage';
|
||||
import ServicePage from './src/pages/ServicePage';
|
||||
import BlogPostPage from './src/pages/BlogPostPage';
|
||||
import { locationData, serviceData, blogPostData } from './src/data/seoData';
|
||||
import { BlogSeoRoute, LocationSeoRoute, ServiceSeoRoute, blogRoutes, locationRoutes, serviceRoutes, legacyRedirects } from './src/routes/seoRoutes';
|
||||
|
||||
// Register GSAP plugins globally
|
||||
gsap.registerPlugin(ScrollTrigger, ScrollToPlugin);
|
||||
|
||||
import HomePage from './src/pages/HomePage';
|
||||
const AboutPage = React.lazy(() => import('./src/pages/AboutPage'));
|
||||
const ServicesPage = React.lazy(() => import('./src/pages/ServicesPage'));
|
||||
const BlogPage = React.lazy(() => import('./src/pages/BlogPage'));
|
||||
const ContactPage = React.lazy(() => import('./src/pages/ContactPage'));
|
||||
const LocationsPage = React.lazy(() => import('./src/pages/LocationsPage'));
|
||||
const PrivacyPolicyPage = React.lazy(() => import('./src/pages/PrivacyPolicyPage'));
|
||||
const TermsOfServicePage = React.lazy(() => import('./src/pages/TermsOfServicePage'));
|
||||
|
||||
// Grain Overlay Component
|
||||
const GrainOverlay = () => (
|
||||
<div className="fixed inset-0 w-full h-full pointer-events-none z-50 opacity-50 dark:opacity-100 bg-grain mix-blend-overlay"></div>
|
||||
);
|
||||
|
||||
const RouteFallback = () => (
|
||||
<div className="min-h-[50vh] flex items-center justify-center px-6 text-sm text-gray-500 dark:text-gray-400">
|
||||
Loading page...
|
||||
</div>
|
||||
);
|
||||
|
||||
const AppContent: React.FC = () => {
|
||||
const location = useLocation();
|
||||
const lenisRef = useRef<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize Lenis for smooth scrolling
|
||||
const lenis = new Lenis({
|
||||
duration: 1.2,
|
||||
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
|
||||
direction: 'vertical',
|
||||
gestureDirection: 'vertical',
|
||||
smooth: true,
|
||||
smoothTouch: false,
|
||||
touchMultiplier: 2,
|
||||
} as any);
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Synchronize Lenis with GSAP ScrollTrigger
|
||||
lenis.on('scroll', ScrollTrigger.update);
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
|
||||
const finePointer = window.matchMedia('(pointer: fine) and (hover: hover)');
|
||||
|
||||
const ticker = (time: number) => {
|
||||
lenis.raf(time * 1000);
|
||||
};
|
||||
if (prefersReducedMotion.matches || !finePointer.matches) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use GSAP ticker for smoother animation loop integration
|
||||
gsap.ticker.add(ticker);
|
||||
let active = true;
|
||||
let ticker: ((time: number) => void) | null = null;
|
||||
|
||||
// Disable lag smoothing to prevent jumps
|
||||
gsap.ticker.lagSmoothing(0);
|
||||
void import('@studio-freight/lenis').then(({ default: Lenis }) => {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset scroll on route change
|
||||
lenis.scrollTo(0, { immediate: true });
|
||||
const lenis = new Lenis({
|
||||
duration: 1.2,
|
||||
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
|
||||
direction: 'vertical',
|
||||
gestureDirection: 'vertical',
|
||||
smooth: true,
|
||||
smoothTouch: false,
|
||||
touchMultiplier: 2,
|
||||
} as any);
|
||||
|
||||
lenisRef.current = lenis;
|
||||
lenis.on('scroll', ScrollTrigger.update);
|
||||
|
||||
ticker = (time: number) => {
|
||||
lenis.raf(time * 1000);
|
||||
};
|
||||
|
||||
gsap.ticker.add(ticker);
|
||||
gsap.ticker.lagSmoothing(0);
|
||||
});
|
||||
|
||||
return () => {
|
||||
gsap.ticker.remove(ticker);
|
||||
lenis.destroy();
|
||||
active = false;
|
||||
if (ticker) {
|
||||
gsap.ticker.remove(ticker);
|
||||
}
|
||||
lenisRef.current?.destroy();
|
||||
lenisRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
lenisRef.current?.scrollTo(0, { immediate: true });
|
||||
}, [location.pathname]);
|
||||
|
||||
return (
|
||||
@@ -67,40 +95,55 @@ const AppContent: React.FC = () => {
|
||||
<GrainOverlay />
|
||||
<Navbar />
|
||||
<main>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/about" element={<AboutPage />} />
|
||||
<Route path="/services" element={<ServicesPage />} />
|
||||
<Route path="/blog" element={<BlogPage />} />
|
||||
<Route path="/contact" element={<ContactPage />} />
|
||||
<Suspense fallback={<RouteFallback />}>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/about" element={<AboutPage />} />
|
||||
<Route path="/services" element={<ServicesPage />} />
|
||||
<Route path="/blog" element={<BlogPage />} />
|
||||
<Route path="/contact" element={<ContactPage />} />
|
||||
<Route path="/locations" element={<LocationsPage />} />
|
||||
<Route path="/privacy-policy" element={<PrivacyPolicyPage />} />
|
||||
<Route path="/terms-of-service" element={<TermsOfServicePage />} />
|
||||
|
||||
{/* SEO Location Pages */}
|
||||
{locationData.map((data) => (
|
||||
<Route
|
||||
key={data.slug}
|
||||
path={`/${data.slug}`}
|
||||
element={<LocationPage data={data} />}
|
||||
/>
|
||||
))}
|
||||
{/* SEO Location Pages */}
|
||||
{locationRoutes.map((data) => (
|
||||
<React.Fragment key={data.slug}>
|
||||
<Route
|
||||
path={data.path}
|
||||
element={<LocationSeoRoute slug={data.slug} />}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
{/* SEO Service Pages */}
|
||||
{serviceData.map((data) => (
|
||||
<Route
|
||||
key={data.slug}
|
||||
path={`/${data.slug}`}
|
||||
element={<ServicePage data={data} />}
|
||||
/>
|
||||
))}
|
||||
{/* SEO Service Pages */}
|
||||
{serviceRoutes.map((data) => (
|
||||
<React.Fragment key={data.slug}>
|
||||
<Route
|
||||
path={data.path}
|
||||
element={<ServiceSeoRoute slug={data.slug} />}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
{/* Authority Blog Posts */}
|
||||
{blogPostData.map((data) => (
|
||||
<Route
|
||||
key={data.slug}
|
||||
path={`/${data.slug}`}
|
||||
element={<BlogPostPage data={data} />}
|
||||
/>
|
||||
))}
|
||||
</Routes>
|
||||
{/* Authority Blog Posts */}
|
||||
{blogRoutes.map((data) => (
|
||||
<React.Fragment key={data.slug}>
|
||||
<Route
|
||||
path={data.path}
|
||||
element={<BlogSeoRoute slug={data.slug} />}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
{/* Legacy URL redirects — preserve old flat URLs */}
|
||||
{legacyRedirects.map(({ from, to }) => (
|
||||
<React.Fragment key={from}>
|
||||
<Route path={from} element={<Navigate to={to} replace />} />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</main>
|
||||
<Footer />
|
||||
<BackToTop />
|
||||
@@ -114,4 +157,4 @@ export default function App() {
|
||||
<AppContent />
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user