From 3cdd8a383029f1ee6f2d4061916bfdd9a3d108c0 Mon Sep 17 00:00:00 2001 From: Yusuf Kaka Date: Sat, 8 Feb 2025 06:57:57 +0200 Subject: [PATCH 1/3] Cleaned up comments --- src/lib/navtools.js | 95 +++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/src/lib/navtools.js b/src/lib/navtools.js index bc933af..eafff74 100755 --- a/src/lib/navtools.js +++ b/src/lib/navtools.js @@ -6,12 +6,13 @@ */ import Swiper from 'swiper/bundle'; import Isotope from 'isotope-layout'; -//import GLightbox from 'glightbox'; -//import Waypoint from 'waypoints/lib/noframework.waypoints.min.js'; export default function Navtools() { /** - * Easy selector helper function + * Selects DOM elements with error handling + * @param {string} el - CSS selector string + * @param {boolean} all - If true, returns all matching elements; if false, returns first match + * @returns {Element|Element[]|null} Single element or array of elements matching the selector */ const select = (el, all = false) => { el = el.trim() @@ -23,7 +24,11 @@ export default function Navtools() { } /** - * Easy event listener function + * Attaches event listeners to DOM elements with support for multiple elements + * @param {string} type - Event type (e.g., 'click', 'submit') + * @param {string} el - CSS selector for target element(s) + * @param {Function} listener - Event handler function + * @param {boolean} all - If true, attaches to all matching elements */ const on = (type, el, listener, all = false) => { let selectEl = select(el, all) @@ -38,7 +43,8 @@ export default function Navtools() { } /** - * Scrolls to an element with header offset + * Smoothly scrolls to the top of the page + * @param {string} el - Target element ID (currently unused in implementation) */ const scrollto = (el) => { window.scrollTo({ @@ -48,18 +54,23 @@ export default function Navtools() { } /** - * Mobile nav toggle + * Handles mobile navigation menu toggle + * Toggles mobile navigation visibility and switches between hamburger/close icons */ - on('click', '.mobile-nav-toggle', function (e) { + on('click', '.mobile-nav-toggle', function(e) { select('#navbar').classList.toggle('navbar-mobile') this.classList.toggle('bi-list') this.classList.toggle('bi-x') }) /** - * Scrool with ofset on links with a class name .scrollto + * Manages navigation link clicks and section visibility + * - Updates active navigation state + * - Handles mobile menu state + * - Controls section visibility animations + * - Manages header styling */ - on('click', '#navbar .nav-link', function (e) { + on('click', '#navbar .nav-link', function(e) { let section = select(this.hash) if (section) { e.preventDefault() @@ -92,7 +103,7 @@ export default function Navtools() { if (!header.classList.contains('header-top')) { header.classList.add('header-top') - setTimeout(function () { + setTimeout(function() { sections.forEach((item) => { item.classList.remove('section-show') }) @@ -111,7 +122,11 @@ export default function Navtools() { }, true) /** - * Activate/show sections on load with hash links + * Initializes page state based on URL hash + * - Sets active navigation item + * - Shows correct section + * - Adjusts header styling + * - Performs smooth scroll to target section */ window.addEventListener('load', () => { if (window.location.hash) { @@ -131,7 +146,7 @@ export default function Navtools() { } }) - setTimeout(function () { + setTimeout(function() { initial_nav.classList.add('section-show') }, 350); @@ -141,24 +156,10 @@ export default function Navtools() { }); /** - * Skills animation - - let skilsContent = select('.skills-content'); - if (skilsContent) { - new Waypoint({ - element: skilsContent, - offset: '80%', - handler: function (direction) { - let progress = select('.progress .progress-bar', true); - progress.forEach((el) => { - el.style.width = el.getAttribute('aria-valuenow') + '%' - }); - } - }) - } - */ - /** - * Testimonials slider + * Initializes testimonials slider with responsive configuration + * - Enables autoplay with 5s delay + * - Configures responsive breakpoints + * - Sets up pagination and navigation */ new Swiper('.testimonials-slider', { speed: 600, @@ -178,7 +179,6 @@ export default function Navtools() { slidesPerView: 1, spaceBetween: 20 }, - 1200: { slidesPerView: 3, spaceBetween: 20 @@ -187,7 +187,10 @@ export default function Navtools() { }); /** - * Porfolio isotope and filter + * Initializes portfolio filtering and layout + * - Sets up Isotope grid layout + * - Handles filter button clicks + * - Updates active filter state */ window.addEventListener('load', () => { let portfolioContainer = select('.portfolio-container'); @@ -199,9 +202,9 @@ export default function Navtools() { let portfolioFilters = select('#portfolio-flters li', true); - on('click', '#portfolio-flters li', function (e) { + on('click', '#portfolio-flters li', function(e) { e.preventDefault(); - portfolioFilters.forEach(function (el) { + portfolioFilters.forEach(function(el) { el.classList.remove('filter-active'); }); this.classList.add('filter-active'); @@ -211,27 +214,12 @@ export default function Navtools() { }); }, true); } - }); /** - * Initiate portfolio lightbox - - const portfolioLightbox = GLightbox({ - selector: '.portfolio-lightbox' - }); - - /** - * Initiate portfolio details lightbox - - const portfolioDetailsLightbox = GLightbox({ - selector: '.portfolio-details-lightbox', - width: '90%', - height: '90vh' - }); - */ - /** - * Portfolio details slider + * Initializes portfolio details slider + * - Enables autoplay with 5s delay + * - Configures navigation and pagination */ new Swiper('.portfolio-details-slider', { speed: 400, @@ -246,5 +234,4 @@ export default function Navtools() { clickable: true } }); - -} \ No newline at end of file +} From a5a9e4f57dcd83f66b308dbfa91c491bd4fedd85 Mon Sep 17 00:00:00 2001 From: Yusuf Kaka Date: Sat, 8 Feb 2025 20:19:15 +0200 Subject: [PATCH 2/3] Fixed!!!! --- index.html | 3 - package-lock.json | 118 +++++++++++++++----- package.json | 1 + src/App.js | 18 ++- src/components/articles.jsx | 202 +++++++++++++++++++++++++--------- src/components/header.jsx | 59 ++++------ src/index.css | 48 ++++---- src/index.js | 11 +- src/lib/navtools.js | 213 ++++++++++++++---------------------- 9 files changed, 389 insertions(+), 284 deletions(-) diff --git a/index.html b/index.html index 6bbc03b..7204218 100755 --- a/index.html +++ b/index.html @@ -17,8 +17,5 @@
-
- Designed by BootstrapMade -
diff --git a/package-lock.json b/package-lock.json index b3968a0..774be58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "react-github-calendar": "^3.2.2", "react-lazyload": "^3.2.0", "react-markdown": "^8.0.2", + "react-router-dom": "^7.1.5", "react-scripts": "^5.0.1", "react-waypoint": "^10.1.0", "rehype-raw": "^7.0.0", @@ -4110,6 +4111,12 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -5953,6 +5960,41 @@ "react": "^16.14.0" } }, + "node_modules/boxicons/node_modules/react-router": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", + "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==", + "license": "MIT", + "dependencies": { + "history": "^4.7.2", + "hoist-non-react-statics": "^2.5.0", + "invariant": "^2.2.4", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.1", + "warning": "^4.0.1" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/boxicons/node_modules/react-router-dom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", + "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==", + "license": "MIT", + "dependencies": { + "history": "^4.7.2", + "invariant": "^2.2.4", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.1", + "react-router": "^4.3.1", + "warning": "^4.0.1" + }, + "peerDependencies": { + "react": ">=15" + } + }, "node_modules/boxicons/node_modules/scheduler": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", @@ -18030,38 +18072,52 @@ } }, "node_modules/react-router": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", - "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.5.tgz", + "integrity": "sha512-8BUF+hZEU4/z/JD201yK6S+UYhsf58bzYIDq2NS1iGpwxSXDu7F+DeGSkIXMFBuHZB21FSiCzEcUb18cQNdRkA==", "license": "MIT", "dependencies": { - "history": "^4.7.2", - "hoist-non-react-statics": "^2.5.0", - "invariant": "^2.2.4", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.1", - "warning": "^4.0.1" + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=15" + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, "node_modules/react-router-dom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", - "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.1.5.tgz", + "integrity": "sha512-/4f9+up0Qv92D3bB8iN5P1s3oHAepSGa9h5k6tpTFlixTTskJZwKGhJ6vRJ277tLD1zuaZTt95hyGWV1Z37csQ==", "license": "MIT", "dependencies": { - "history": "^4.7.2", - "invariant": "^2.2.4", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.1", - "react-router": "^4.3.1", - "warning": "^4.0.1" + "react-router": "7.1.5" + }, + "engines": { + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=15" + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" } }, "node_modules/react-scripts": { @@ -19568,6 +19624,12 @@ "node": ">= 0.8.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -21100,6 +21162,12 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "license": "0BSD" }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -21230,9 +21298,9 @@ } }, "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "license": "Apache-2.0", "peer": true, "bin": { @@ -21240,7 +21308,7 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=14.17" + "node": ">=4.2.0" } }, "node_modules/unbox-primitive": { diff --git a/package.json b/package.json index 611e256..a70e402 100755 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "react-github-calendar": "^3.2.2", "react-lazyload": "^3.2.0", "react-markdown": "^8.0.2", + "react-router-dom": "^7.1.5", "react-scripts": "^5.0.1", "react-waypoint": "^10.1.0", "rehype-raw": "^7.0.0", diff --git a/src/App.js b/src/App.js index 3e9caf0..6ac3143 100755 --- a/src/App.js +++ b/src/App.js @@ -5,17 +5,23 @@ import Resume from './components/resume'; import Repositories from './components/repos'; import Articles from './components/articles'; import Portfolio from './components/portfolio'; +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; function App() { return ( +
-
- - - - - +
+ + } /> + } /> + } /> + } /> + } /> + } /> +
+
); } diff --git a/src/components/articles.jsx b/src/components/articles.jsx index 4f48460..1d50614 100755 --- a/src/components/articles.jsx +++ b/src/components/articles.jsx @@ -1,8 +1,15 @@ import React, { Component } from 'react'; +import { useParams, Link } from 'react-router-dom'; import LazyLoad from 'react-lazyload'; import ReactMarkdown from 'react-markdown'; import rehypeRaw from 'rehype-raw'; +// Wrapper component to access URL parameters +const ArticlesWrapper = (props) => { + const params = useParams(); + return ; +}; + function parseMetadata(text) { const metadata = {}; const lines = text.split('\n'); @@ -21,31 +28,52 @@ function parseMetadata(text) { } catch (error) { console.error('Error parsing metadata:', error); } + // Add slug based on title if not provided + if (!metadata.slug && metadata.title) { + metadata.slug = metadata.title + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/(^-|-$)/g, ''); + } return metadata; } -export default class Articles extends Component { +class Articles extends Component { constructor(props) { super(props); this.state = { loading: true, articles: [], + currentPage: 1, + articlesPerPage: 5, error: null, }; this.abortController = new AbortController(); } componentDidMount = async () => { + await this.fetchArticles(); + }; + + componentDidUpdate(prevProps) { + if (prevProps.slug !== this.props.slug) { + // Reset page when slug changes + this.setState({ currentPage: 1 }); + } + } + + componentWillUnmount() { + this.abortController.abort(); + } + + fetchArticles = async () => { const markdownFiles = []; const dataDir = '/data/articles'; try { - // Fetch the index.json file const response = await fetch(`${dataDir}/index.json`); const files = await response.json(); - console.log('Files:', files); // Debug: Log the list of files - for (const file of files) { const fileResponse = await fetch(`${dataDir}/${file}`); const markdownText = await fileResponse.text(); @@ -56,72 +84,138 @@ export default class Articles extends Component { }); } - // Sort the markdownFiles array by date in descending order markdownFiles.sort((a, b) => new Date(b.metadata.date) - new Date(a.metadata.date)); - console.log('Markdown Files:', markdownFiles); // Debug: Log the markdown files - this.setState({ articles: markdownFiles, loading: false }); } catch (error) { - console.error('Error fetching markdown files:', error); // Debug: Log any errors + console.error('Error fetching markdown files:', error); this.setState({ error, loading: false }); } }; - componentWillUnmount() { - this.abortController.abort(); - } + handlePageChange = (pageNumber) => { + this.setState({ currentPage: pageNumber }); + }; + + renderPagination = (totalPages) => { + const { currentPage } = this.state; + const pages = []; + + for (let i = 1; i <= totalPages; i++) { + pages.push( + + ); + } + + return ( +
+ {pages} +
+ ); + }; render() { - const { articles } = this.state; + const { articles, currentPage, articlesPerPage, loading } = this.state; + const { slug } = this.props; + + if (loading) { + return
Loading...
; + } + + // If slug is provided, show single article + if (slug) { + const article = articles.find(a => a.metadata.slug === slug); + if (!article) { + return
Article not found
; + } - if (!this.state.loading) { return ( -
- {/* ======= Articles Section ======= */} -
-
-
-
-
-

Articles

+
+
+
+
+
+
+

{article.metadata.title}

-
- {articles.map((article, index) => ( - -
-
-

{article.metadata.title}

-
-
- -

Published: {article.metadata.date}

-
-
-
- ))} -
-
-
-
-

Article List

-
-
- +
+ +

Published: {article.metadata.date}

+ + Back to Articles +
-
-
+
+
); - } else { - return
Loading...
; } + + // Calculate pagination + const indexOfLastArticle = currentPage * articlesPerPage; + const indexOfFirstArticle = indexOfLastArticle - articlesPerPage; + const currentArticles = articles.slice(indexOfFirstArticle, indexOfLastArticle); + const totalPages = Math.ceil(articles.length / articlesPerPage); + + return ( +
+
+
+
+
+

Articles

+
+
+ {currentArticles.map((article, index) => ( + +
+
+ +

{article.metadata.title}

+ +
+
+ +

Published: {article.metadata.date}

+
+
+
+ ))} +
+ {this.renderPagination(totalPages)} +
+
+
+

Article List

+
+
+
    + {articles.map((article) => ( +
  • + + {article.metadata.title} + +
  • + ))} +
+
+
+
+
+
+ ); } -} \ No newline at end of file +} + +export default ArticlesWrapper; \ No newline at end of file diff --git a/src/components/header.jsx b/src/components/header.jsx index a31c491..240e32c 100755 --- a/src/components/header.jsx +++ b/src/components/header.jsx @@ -1,90 +1,73 @@ -import React, { Component } from 'react' - +import React, { Component } from 'react'; +import { Link } from 'react-router-dom'; export default class Header extends Component { - render() { return (
- {/* ======= Header ======= */} - {/* End Header */}
- ) + ); } } diff --git a/src/index.css b/src/index.css index 36354b0..1573564 100755 --- a/src/index.css +++ b/src/index.css @@ -56,7 +56,7 @@ h6 { /*-------------------------------------------------------------- # Header --------------------------------------------------------------*/ -#header { +header { transition: ease-in-out 0.3s; position: relative; height: 100vh; @@ -66,11 +66,11 @@ h6 { overflow-y: auto; } -#header * { +header * { transition: ease-in-out 0.3s; } -#header h1 { +header h1 { font-size: 54px; margin: 0; padding: 0; @@ -80,43 +80,43 @@ h6 { font-family: 'Marck Script'; } -#header h1 a, -#header h1 a:hover { +header h1 a, +header h1 a:hover { color: #fff; line-height: 1; display: inline-block; } -#header h2 { +header h2 { font-size: 24px; margin-top: 20px; color: rgba(255, 255, 255, 0.8); } -#header h2 span { +header h2 span { color: #fff; border-bottom: 2px solid #1888d2; padding-bottom: 6px; } -#header img { +header img { padding: 0; margin: 0; } -#header .container { +header .container { display: flex; flex-direction: column; align-items: center; background: rgba(0, 0, 0, 0.7); } -#header .social-links { +header .social-links { margin-top: 40px; display: flex; } -#header .social-links a { +header .social-links a { font-size: 16px; display: flex; justify-content: center; @@ -130,25 +130,25 @@ h6 { height: 40px; } -#header .social-links a:hover { +header .social-links a:hover { background: #1888d2; } @media (max-width: 992px) { - #header h1 { + header h1 { font-size: 36px; } - #header h2 { + header h2 { font-size: 20px; line-height: 30px; } - #header .social-links { + header .social-links { margin-top: 15px; } - #header .container { + header .container { display: flex; flex-direction: column; align-items: center; @@ -157,7 +157,7 @@ h6 { } /* Header Top */ -#header.header-top { +header.header-top { height: 80px; position: fixed; left: 0; @@ -166,31 +166,31 @@ h6 { background: rgba(0, 0, 0, 0.9); } -#header.header-top .social-links, -#header.header-top h2 { +header.header-top .social-links, +header.header-top h2 { display: none; } -#header.header-top h1 { +header.header-top h1 { margin-right: auto; font-size: 36px; } -#header.header-top .container { +header.header-top .container { display: flex; align-items: center; } -#header.header-top .navbar { +header.header-top .navbar { margin: 0; } @media (max-width: 768px) { - #header.header-top { + header.header-top { height: 60px; } - #header.header-top h1 { + header.header-top h1 { font-size: 26px; } } diff --git a/src/index.js b/src/index.js index 98df454..10c5b84 100755 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ import React from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot } from 'react-dom/client'; +//import ReactDOM from 'react-dom'; import 'bootstrap/dist/css/bootstrap.css'; import 'bootstrap-icons/font/bootstrap-icons.css'; import 'boxicons/css/boxicons.css'; @@ -14,11 +15,13 @@ import ReactGA from 'react-ga4'; ReactGA.initialize('G-J0X6EB8JL4'); -ReactDOM.render( +const container = document.getElementById('root'); +const root = createRoot(container); + +root.render( - , - document.getElementById('root') + ); Navtools(); diff --git a/src/lib/navtools.js b/src/lib/navtools.js index eafff74..95f4bf2 100755 --- a/src/lib/navtools.js +++ b/src/lib/navtools.js @@ -1,19 +1,7 @@ -/** -* Template Name: Personal - v4.7.0 -* Template URL: https://bootstrapmade.com/personal-free-resume-bootstrap-template/ -* Author: BootstrapMade.com -* License: https://bootstrapmade.com/license/ -*/ import Swiper from 'swiper/bundle'; import Isotope from 'isotope-layout'; export default function Navtools() { - /** - * Selects DOM elements with error handling - * @param {string} el - CSS selector string - * @param {boolean} all - If true, returns all matching elements; if false, returns first match - * @returns {Element|Element[]|null} Single element or array of elements matching the selector - */ const select = (el, all = false) => { el = el.trim() if (all) { @@ -22,136 +10,101 @@ export default function Navtools() { return document.querySelector(el) } } - - /** - * Attaches event listeners to DOM elements with support for multiple elements - * @param {string} type - Event type (e.g., 'click', 'submit') - * @param {string} el - CSS selector for target element(s) - * @param {Function} listener - Event handler function - * @param {boolean} all - If true, attaches to all matching elements - */ const on = (type, el, listener, all = false) => { - let selectEl = select(el, all) - - if (selectEl) { - if (all) { - selectEl.forEach(e => e.addEventListener(type, listener)) - } else { - selectEl.addEventListener(type, listener) + // Add debug log + console.log('Setting up listener for:', el); + + document.addEventListener(type, (e) => { + // Add debug log + console.log('Event triggered:', e.target); + const target = e.target.closest(el); + // Add debug log + console.log('Closest match:', target); + + if (target) { + listener.call(target, e); } - } - } - - /** - * Smoothly scrolls to the top of the page - * @param {string} el - Target element ID (currently unused in implementation) - */ - const scrollto = (el) => { - window.scrollTo({ - top: 0, - behavior: 'smooth' - }) + }); } - /** - * Handles mobile navigation menu toggle - * Toggles mobile navigation visibility and switches between hamburger/close icons - */ - on('click', '.mobile-nav-toggle', function(e) { - select('#navbar').classList.toggle('navbar-mobile') - this.classList.toggle('bi-list') - this.classList.toggle('bi-x') - }) - - /** - * Manages navigation link clicks and section visibility - * - Updates active navigation state - * - Handles mobile menu state - * - Controls section visibility animations - * - Manages header styling - */ - on('click', '#navbar .nav-link', function(e) { - let section = select(this.hash) - if (section) { - e.preventDefault() - - let navbar = select('#navbar') - let header = select('#header') - let sections = select('section', true) - let navlinks = select('#navbar .nav-link', true) + // Add mobile nav toggle handler + on('click', 'i.mobile-nav-toggle', function(e) { + console.log('Mobile toggle clicked'); + let navbar = select('nav'); + navbar.classList.toggle('navbar-mobile'); + this.classList.toggle('bi-list'); + this.classList.toggle('bi-x'); + }); - navlinks.forEach((item) => { - item.classList.remove('active') + // Modified navigation link handler + on('click', 'a.nav-link', function(e) { + console.log("Clicky"); + e.preventDefault(); + const targetPath = this.getAttribute('href'); + console.log('Target path:', targetPath); + + let navbar = select('nav'); + let header = select('header'); + let sections = select('section', true); + let navlinks = select('.nav-link', true); + + // Update active states + navlinks.forEach((item) => { + item.classList.remove('active') + }) + this.classList.add('active') + + // Handle mobile menu + if (navbar.classList.contains('navbar-mobile')) { + navbar.classList.remove('navbar-mobile') + let navbarToggle = select('.mobile-nav-toggle') + navbarToggle.classList.toggle('bi-list') + navbarToggle.classList.toggle('bi-x') + } + + // Handle header and sections + if (targetPath === '/') { + header.classList.remove('header-top') + sections.forEach((item) => { + item.classList.remove('section-show') }) - - this.classList.add('active') - - if (navbar.classList.contains('navbar-mobile')) { - navbar.classList.remove('navbar-mobile') - let navbarToggle = select('.mobile-nav-toggle') - navbarToggle.classList.toggle('bi-list') - navbarToggle.classList.toggle('bi-x') - } - - if (this.hash === '#header') { - header.classList.remove('header-top') - sections.forEach((item) => { - item.classList.remove('section-show') - }) - return; - } - - if (!header.classList.contains('header-top')) { - header.classList.add('header-top') - setTimeout(function() { - sections.forEach((item) => { - item.classList.remove('section-show') - }) - section.classList.add('section-show') - - }, 350); - } else { + } else { + header.classList.add('header-top') + + // Always use setTimeout for consistency + setTimeout(() => { sections.forEach((item) => { item.classList.remove('section-show') }) - section.classList.add('section-show') - } - - scrollto(this.hash) + const sectionId = targetPath.replace('/', ''); + const targetSection = select(`section#${sectionId}`); + console.log('Looking for section with ID:', sectionId); + console.log('Found section:', targetSection); + if (targetSection) { + targetSection.classList.add('section-show'); + } + }, 350); } - }, true) - - /** - * Initializes page state based on URL hash - * - Sets active navigation item - * - Shows correct section - * - Adjusts header styling - * - Performs smooth scroll to target section - */ - window.addEventListener('load', () => { - if (window.location.hash) { - let initial_nav = select(window.location.hash) - - if (initial_nav) { - let header = select('#header') - let navlinks = select('#navbar .nav-link', true) + }) + - header.classList.add('header-top') - navlinks.forEach((item) => { - if (item.getAttribute('href') === window.location.hash) { - item.classList.add('active') - } else { - item.classList.remove('active') - } - }) + // Modified initial load handler + window.addEventListener('load', () => { + const currentPath = window.location.pathname; + if (currentPath !== '/') { + let header = select('header') + let navlinks = select('nav-link', true) - setTimeout(function() { - initial_nav.classList.add('section-show') - }, 350); + header.classList.add('header-top') - scrollto(window.location.hash) - } + navlinks.forEach((item) => { + if (item.getAttribute('href') === currentPath) { + item.classList.add('active') + } else { + item.classList.remove('active') + } + }) } }); @@ -202,9 +155,9 @@ export default function Navtools() { let portfolioFilters = select('#portfolio-flters li', true); - on('click', '#portfolio-flters li', function(e) { + on('click', '#portfolio-flters li', function (e) { e.preventDefault(); - portfolioFilters.forEach(function(el) { + portfolioFilters.forEach(function (el) { el.classList.remove('filter-active'); }); this.classList.add('filter-active'); From 6b18b12370eb7d35ddd2123787b5bca730a6e6f4 Mon Sep 17 00:00:00 2001 From: Yusuf Kaka Date: Sat, 8 Feb 2025 20:27:13 +0200 Subject: [PATCH 3/3] Fixed mobile list --- src/components/articles.jsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/components/articles.jsx b/src/components/articles.jsx index 1d50614..52f8701 100755 --- a/src/components/articles.jsx +++ b/src/components/articles.jsx @@ -142,7 +142,7 @@ class Articles extends Component {
-
+

{article.metadata.title}

@@ -156,6 +156,22 @@ class Articles extends Component {
+
+
+

Article List

+
+
+
    + {articles.map((article) => ( +
  • + + {article.metadata.title} + +
  • + ))} +
+
+