Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added back to top button #151 #153

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ body,
font-family: "DM Sans", sans-serif;
}

body{
body {
background: #F1EADC;
}

Expand All @@ -30,4 +30,4 @@ input[type="radio"] {
cursor: pointer;
font-size: 2rem;
margin-bottom: 0;
}
}
24 changes: 13 additions & 11 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,23 @@ import Footer from "../src/components/Shared/Footer";
import { Outlet } from "react-router-dom";
import { AuthProvider } from './components/Shared/AuthContext';
import { KindeProvider } from "@kinde-oss/kinde-auth-react";
import BackToTopButton from "./components/Shared/BackToTopButton";

function App() {
return (
<AuthProvider>
<KindeProvider
clientId={import.meta.env.VITE_KINDE_CLIENT_ID}
domain={import.meta.env.VITE_KINDE_DOMAIN}
redirectUri={import.meta.env.VITE_KINDE_REDIRECT_URI}
logoutUri={import.meta.env.VITE_KINDE_LOGOUT_REDIRECT_URI}
>
<Navbar />
<Outlet />
<Footer />
</KindeProvider>
</AuthProvider>
<KindeProvider
clientId={import.meta.env.VITE_KINDE_CLIENT_ID}
domain={import.meta.env.VITE_KINDE_DOMAIN}
redirectUri={import.meta.env.VITE_KINDE_REDIRECT_URI}
logoutUri={import.meta.env.VITE_KINDE_LOGOUT_REDIRECT_URI}
>
<BackToTopButton />
<Navbar />
<Outlet />
<Footer />
</KindeProvider>
</AuthProvider>

);
}
Expand Down
58 changes: 58 additions & 0 deletions frontend/src/components/Shared/BackToTopButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useState, useEffect } from 'react';

const BackToTopButton = () => {
const [isVisible, setIsVisible] = useState(false);
const [shouldRender, setShouldRender] = useState(false); // To control button rendering

// Show button when the user scrolls down 300px
const toggleVisibility = () => {
if (window.scrollY > 300) {
setShouldRender(true); // Start rendering the button
setIsVisible(true); // Make it visible (for fade-in)
} else {
setIsVisible(false); // Hide button (fade-out)
}
};
RamakrushnaBiswal marked this conversation as resolved.
Show resolved Hide resolved

// Scroll to the top of the page
const scrollToTop = () => {
window.scrollTo({
top: 0,
behavior: 'smooth', // Smooth scroll to the top
});
};
RamakrushnaBiswal marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
window.addEventListener('scroll', toggleVisibility);

// Cleanup event listener on component unmount
return () => {
window.removeEventListener('scroll', toggleVisibility);
};
}, []);
Comment on lines +25 to +32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using useCallback for the toggleVisibility function.

The useEffect hook correctly adds and removes the scroll event listener. However, to prevent unnecessary re-renders and ensure consistent behavior, consider wrapping toggleVisibility with useCallback.

Here's a suggested implementation:

const toggleVisibility = useCallback(() => {
  setIsVisible(window.scrollY > 300);
}, []);

useEffect(() => {
  window.addEventListener('scroll', toggleVisibility);
  return () => window.removeEventListener('scroll', toggleVisibility);
}, [toggleVisibility]);

This ensures that the function reference remains stable across re-renders, optimizing performance and preventing potential bugs.


// Remove the button from the DOM after fade-out animation ends
useEffect(() => {
if (!isVisible) {
const timeout = setTimeout(() => setShouldRender(false), 500); // Match the animation duration
return () => clearTimeout(timeout); // Clean up the timeout
}
}, [isVisible]);

return (
<>
{shouldRender && (
<button
onClick={scrollToTop}
className={`z-50 fixed bottom-12 right-8 bg-green-700 hover:bg-green-600 text-white font-bold px-5 py-3 rounded-md shadow-lg hover:shadow-xl transition-all duration-300 transform hover:scale-110 ${isVisible ? 'animate-fadeInBounce' : 'animate-fadeOutBounce'
}`}
style={{ boxShadow: "0 4px 8px rgba(0, 0, 0, 0.2)" }}
>
</button>
)}
</>
);
};

export default BackToTopButton;
RamakrushnaBiswal marked this conversation as resolved.
Show resolved Hide resolved
18 changes: 9 additions & 9 deletions frontend/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ export default {
'roboto': ['"Roboto Serif"', 'sans-serif'],
},
keyframes: {
fadeIn: {
'0%': { opacity: 0 },
'100%': { opacity: 1 },
fadeInBounce: {
'0%': { opacity: 0, transform: 'translateY(40px)' },
'100%': { opacity: 1, transform: 'translateY(0)' },
},
slideIn: {
'0%': { transform: 'translateX(100%)' },
'100%': { transform: 'translateX(0)' },
fadeOutBounce: {
'0%': { opacity: 1, transform: 'translateY(0)' },
'100%': { opacity: 0, transform: 'translateY(40px)' },
},
},
animation: {
fadeIn: 'fadeIn 2s ease-in-out',
slideIn: 'slideIn 1.5s ease-in-out',
fadeInBounce: 'fadeInBounce 0.5s ease-out forwards',
fadeOutBounce: 'fadeOutBounce 0.5s ease-out forwards',
},
},
},
plugins: [],
}
};
Loading