diff --git a/.all-contributorsrc b/.all-contributorsrc
index 7b9c595..08b3697 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -180,6 +180,13 @@
"avatar_url": "https://avatars.githubusercontent.com/u/166863746?v=4",
"profile": "https://madebynitin.netlify.app/",
"contributions": ["code", "bug"]
+ },
+ {
+ "login": "Vamshavardhan50",
+ "name": "Vamsha vardhan",
+ "avatar_url": "https://avatars.githubusercontent.com/u/162793177?v=4",
+ "profile": "https://github.com/Vamshavardhan50",
+ "contributions": ["code", "bug"]
}
]
}
diff --git a/.github/workflows/prchecks.yml b/.github/workflows/prchecks.yml
index 7b849bf..95ad8fd 100644
--- a/.github/workflows/prchecks.yml
+++ b/.github/workflows/prchecks.yml
@@ -10,6 +10,9 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
+ with:
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ ref: ${{ github.event.pull_request.head.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4
diff --git a/.github/workflows/prcontributorbot.yaml b/.github/workflows/prcontributorbot.yaml
index 2c048b5..51dfd42 100644
--- a/.github/workflows/prcontributorbot.yaml
+++ b/.github/workflows/prcontributorbot.yaml
@@ -9,6 +9,7 @@ on:
jobs:
format:
runs-on: ubuntu-latest
+ if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
permissions:
contents: write
@@ -17,7 +18,8 @@ jobs:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
- ref: ${{ github.head_ref }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ ref: ${{ github.event.pull_request.head.sha }}
- name: Setup Node
uses: actions/setup-node@v4
diff --git a/.husky/pre-commit b/.husky/pre-commit
index 4e058a5..ba403f4 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,5 +1,2 @@
-#!/usr/bin/env sh
-. "$(dirname -- "$0")/_/husky.sh"
-
npm run pre-commit
npx lint-staged
\ No newline at end of file
diff --git a/app/(core)/components/Header.jsx b/app/(core)/components/Header.jsx
index dc45246..12456db 100644
--- a/app/(core)/components/Header.jsx
+++ b/app/(core)/components/Header.jsx
@@ -1,6 +1,6 @@
// app/components/Header.jsx
"use client";
-import { useState, useCallback } from "react";
+import { useState, useCallback, useEffect, useRef } from "react";
import useTranslation from "../hooks/useTranslation.ts";
import { Logo } from "./Logo";
import NavMenu from "./Nav";
@@ -20,6 +20,8 @@ export default function Header() {
const pathname = usePathname();
const { t, meta } = useTranslation();
const isCompleted = meta?.completed || false;
+ const bodyStylesRef = useRef({ overflow: "", paddingRight: "" });
+ const previousActiveRef = useRef(null);
// Close menu when route changes
const [prevPathname, setPrevPathname] = useState(pathname);
@@ -29,6 +31,84 @@ export default function Header() {
}
const handleMenuToggle = useCallback(() => setMenuOpen((open) => !open), []);
+ const handleMenuClose = useCallback(() => setMenuOpen(false), []);
+
+ useEffect(() => {
+ if (typeof document === "undefined") return;
+
+ const body = document.body;
+ if (isMenuOpen) {
+ bodyStylesRef.current = {
+ overflow: body.style.overflow,
+ paddingRight: body.style.paddingRight,
+ };
+
+ const scrollbarWidth =
+ window.innerWidth - document.documentElement.clientWidth;
+
+ body.style.overflow = "hidden";
+ body.style.paddingRight = scrollbarWidth ? `${scrollbarWidth}px` : "";
+ } else {
+ body.style.overflow = bodyStylesRef.current.overflow;
+ body.style.paddingRight = bodyStylesRef.current.paddingRight;
+ }
+
+ return () => {
+ body.style.overflow = bodyStylesRef.current.overflow;
+ body.style.paddingRight = bodyStylesRef.current.paddingRight;
+ };
+ }, [isMenuOpen]);
+
+ useEffect(() => {
+ if (!isMenuOpen || typeof document === "undefined") return;
+
+ const nav = document.querySelector(".nav-menu");
+ if (!nav) return;
+
+ const focusableSelector =
+ "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])";
+
+ const focusable = Array.from(nav.querySelectorAll(focusableSelector));
+
+ previousActiveRef.current = document.activeElement;
+
+ requestAnimationFrame(() => {
+ const firstFocusable = focusable[0];
+ if (firstFocusable && typeof firstFocusable.focus === "function") {
+ firstFocusable.focus();
+ }
+ });
+
+ const handleKeyDown = (event) => {
+ if (event.key === "Escape") {
+ handleMenuClose();
+ return;
+ }
+
+ if (event.key !== "Tab" || focusable.length === 0) return;
+
+ const firstFocusable = focusable[0];
+ const lastFocusable = focusable[focusable.length - 1];
+
+ if (event.shiftKey && document.activeElement === firstFocusable) {
+ event.preventDefault();
+ lastFocusable.focus();
+ } else if (!event.shiftKey && document.activeElement === lastFocusable) {
+ event.preventDefault();
+ firstFocusable.focus();
+ }
+ };
+
+ document.addEventListener("keydown", handleKeyDown);
+ return () => {
+ document.removeEventListener("keydown", handleKeyDown);
+
+ const previousActive = previousActiveRef.current;
+ if (previousActive && typeof previousActive.focus === "function") {
+ previousActive.focus();
+ }
+ };
+ }, [isMenuOpen, handleMenuClose]);
return (
-
+
@@ -54,6 +134,13 @@ export default function Header() {
+
+
);
}
diff --git a/app/(core)/components/Nav.tsx b/app/(core)/components/Nav.tsx
index 21e2241..e425da1 100644
--- a/app/(core)/components/Nav.tsx
+++ b/app/(core)/components/Nav.tsx
@@ -2,6 +2,8 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useEffect, useRef, useState, useCallback } from "react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faXmark } from "@fortawesome/free-solid-svg-icons";
import useTranslation from "../../(core)/hooks/useTranslation";
const menuItems = [
@@ -12,7 +14,11 @@ const menuItems = [
{ href: "/contribute", label: "Contribute" },
];
-export default function NavMenu() {
+type NavMenuProps = {
+ onNavigate?: () => void;
+};
+
+export default function NavMenu({ onNavigate }: NavMenuProps) {
const pathname = usePathname();
const navRef = useRef(null);
const [underlineStyle, setUnderlineStyle] = useState({ left: 0, width: 0 });
@@ -44,14 +50,27 @@ export default function NavMenu() {
return () => window.removeEventListener("resize", updateUnderline);
}, [updateUnderline]);
+ const handleNavigate = useCallback(() => {
+ onNavigate?.();
+ }, [onNavigate]);
+
return (