diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 79ce58d..c368a53 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -27,8 +27,8 @@ }, "devDependencies": { "@eslint/js": "^9.33.0", - "@types/react": "^19.1.10", - "@types/react-dom": "^19.1.7", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^5.0.0", "eslint": "^9.33.0", "eslint-plugin-react-hooks": "^5.2.0", @@ -1752,23 +1752,30 @@ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, "node_modules/@types/react": { - "version": "19.1.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", - "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", + "version": "18.3.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", + "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", "license": "MIT", "dependencies": { - "csstype": "^3.0.2" + "@types/prop-types": "*", + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "19.1.9", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", - "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^18.0.0" } }, "node_modules/@types/trusted-types": { @@ -2171,9 +2178,9 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/debug": { diff --git a/frontend/src/pages/OpenSource/OSSBlog.jsx b/frontend/src/pages/OpenSource/OSSBlog.jsx index e784a7f..485e41f 100644 --- a/frontend/src/pages/OpenSource/OSSBlog.jsx +++ b/frontend/src/pages/OpenSource/OSSBlog.jsx @@ -17,6 +17,25 @@ const OSSBlog = () => { const [error, setError] = useState(""); const [searchQuery, setSearchQuery] = useState(""); const [filteredArticles, setFilteredArticles] = useState([]); + const [showScrollTop, setShowScrollTop] = useState(false); + const scrollRef = React.useRef(null); + useEffect(() => { + const el = scrollRef.current; + + const handleScroll = () => { + setShowScrollTop(el.scrollTop > 100); + }; + + el.addEventListener("scroll", handleScroll); + return () => el.removeEventListener("scroll", handleScroll); +}, []); +const scrollToTop = () => { + scrollRef.current.scrollTo({ + top: 0, + behavior: "smooth", + }); +}; + useEffect(() => { fetchArticles(); @@ -86,7 +105,29 @@ const OSSBlog = () => { }; return ( -
+
+ {showScrollTop && ( + + )}
{/* Header */} { const [searchQuery, setSearchQuery] = useState(""); const [monthFilter, setMonthFilter] = useState("all"); const [modeFilter, setModeFilter] = useState("all"); + const [showScrollTop, setShowScrollTop] = useState(false); + const scrollRef = useRef(null); + useEffect(() => { + const el = scrollRef.current; + if (!el) return; + + const handleScroll = () => { + setShowScrollTop(el.scrollTop > 200); + }; + + el.addEventListener("scroll", handleScroll); + return () => el.removeEventListener("scroll", handleScroll); + }, []); + const scrollToTop = () => { + scrollRef.current?.scrollTo({ top: 0, behavior: "smooth" }); + }; + + useEffect(() => { const fetchEvents = async () => { setLoading(true); @@ -261,7 +279,33 @@ const OpenSourceEvents = () => { ]; return ( -
+
+ {showScrollTop && ( + + )}