File tree Expand file tree Collapse file tree 3 files changed +59
-27
lines changed Expand file tree Collapse file tree 3 files changed +59
-27
lines changed Original file line number Diff line number Diff line change 11import { useEffect } from 'react' ;
22import { RouterProvider } from 'react-router-dom' ;
33import router from '@/routes' ;
4+ import ScrollToTop from '@/components/shared/ScrollToTop' ;
45
56const App = ( ) => {
67 useEffect ( ( ) => {
@@ -25,6 +26,7 @@ const App = () => {
2526 return (
2627 < div className = "min-h-screen flex flex-col bg-white dark:bg-gray-900" >
2728 < RouterProvider router = { router } />
29+ < ScrollToTop />
2830 </ div >
2931 ) ;
3032} ;
Original file line number Diff line number Diff line change 1+ import { useState , useEffect } from 'react' ;
2+ import { motion , AnimatePresence } from 'framer-motion' ;
3+ import { ChevronUp } from 'lucide-react' ;
4+
5+ const ScrollToTop = ( ) => {
6+ const [ isVisible , setIsVisible ] = useState ( false ) ;
7+
8+ useEffect ( ( ) => {
9+ const toggleVisibility = ( ) => {
10+ if ( window . scrollY > 300 ) {
11+ setIsVisible ( true ) ;
12+ } else {
13+ setIsVisible ( false ) ;
14+ }
15+ } ;
16+
17+ window . addEventListener ( 'scroll' , toggleVisibility ) ;
18+
19+ return ( ) => {
20+ window . removeEventListener ( 'scroll' , toggleVisibility ) ;
21+ } ;
22+ } , [ ] ) ;
23+
24+ const scrollToTop = ( ) => {
25+ window . scrollTo ( {
26+ top : 0 ,
27+ behavior : 'smooth' ,
28+ } ) ;
29+ } ;
30+
31+ return (
32+ < AnimatePresence >
33+ { isVisible && (
34+ < motion . button
35+ onClick = { scrollToTop }
36+ className = "fixed bottom-6 right-6 sm:bottom-8 sm:right-8 z-50 p-3 sm:p-4 bg-gradient-to-r from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 text-white rounded-full shadow-lg hover:shadow-xl transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-offset-2 dark:focus:ring-offset-gray-900"
37+ aria-label = "Scroll to top"
38+ initial = { { opacity : 0 , scale : 0.5 , y : 20 } }
39+ animate = { { opacity : 1 , scale : 1 , y : 0 } }
40+ exit = { { opacity : 0 , scale : 0.5 , y : 20 } }
41+ whileHover = { { scale : 1.1 , y : - 2 } }
42+ whileTap = { { scale : 0.95 } }
43+ transition = { {
44+ type : 'spring' ,
45+ stiffness : 500 ,
46+ damping : 30 ,
47+ } }
48+ >
49+ < ChevronUp className = "h-5 w-5 sm:h-6 sm:w-6" aria-hidden = "true" />
50+ </ motion . button >
51+ ) }
52+ </ AnimatePresence >
53+ ) ;
54+ } ;
55+
56+ export default ScrollToTop ;
57+
Original file line number Diff line number Diff line change @@ -361,33 +361,6 @@ const NewsDetailPage: React.FC = () => {
361361 </ div >
362362 </ div >
363363 ) }
364-
365- { /* Back to Top Button */ }
366- < div className = "fixed bottom-8 right-8 z-50" >
367- < motion . button
368- onClick = { ( ) => window . scrollTo ( { top : 0 , behavior : 'smooth' } ) }
369- className = "p-3 bg-blue-600 text-white rounded-full shadow-lg hover:bg-blue-700 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-400"
370- aria-label = "Back to top"
371- whileHover = { { scale : 1.05 } }
372- whileTap = { { scale : 0.95 } }
373- >
374- < svg
375- xmlns = "http://www.w3.org/2000/svg"
376- className = "h-6 w-6"
377- fill = "none"
378- viewBox = "0 0 24 24"
379- stroke = "currentColor"
380- aria-hidden = "true"
381- >
382- < path
383- strokeLinecap = "round"
384- strokeLinejoin = "round"
385- strokeWidth = { 2 }
386- d = "M5 15l7-7 7 7"
387- />
388- </ svg >
389- </ motion . button >
390- </ div >
391364 </ div >
392365
393366 { /* Image Modal */ }
You can’t perform that action at this time.
0 commit comments