my changes

This commit is contained in:
2026-01-09 00:07:39 +03:00
parent 6420af403a
commit 7ad7dff0da
24 changed files with 2345 additions and 727 deletions

View File

@ -1,21 +1,19 @@
import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-scroll";
import LanguageSwitcher from "../LanguageSwitcher/LanguageSwitcher";
import "../../index.css";
const Navbar = () => {
const { t, i18n } = useTranslation();
const [menuOpen, setMenuOpen] = useState(false);
const [activeSection, setActiveSection] = useState("home");
const [isDarkMode, setIsDarkMode] = useState(false);
const [isDarkMode, setIsDarkMode] = useState(false);
// Initialize dark mode
useEffect(() => {
const savedTheme = localStorage.getItem("theme");
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
const shouldEnableDarkMode = savedTheme === "dark" || (!savedTheme && prefersDark);
setIsDarkMode(shouldEnableDarkMode);
if (shouldEnableDarkMode) {
document.documentElement.classList.add("dark");
@ -26,11 +24,10 @@ const Navbar = () => {
setMenuOpen(!menuOpen);
};
// Toggle dark/light mode
const toggleDarkMode = () => {
const newDarkMode = !isDarkMode;
setIsDarkMode(newDarkMode);
if (newDarkMode) {
document.documentElement.classList.add("dark");
localStorage.setItem("theme", "dark");
@ -40,25 +37,19 @@ const Navbar = () => {
}
};
// Handle scroll to update active section
useEffect(() => {
const handleScroll = () => {
const sections = ["home", "services", "about", "contact"];
const scrollPosition = window.scrollY + 100;
const sections = ["home", "services", "about", "contact", "sections"];
const scrollPosition = window.scrollY + 120; // offset to detect current section
for (const section of sections) {
const element =
document.getElementById(section) ||
document.querySelector(`[name="${section}"]`);
document.getElementById(section) || document.querySelector(`[name="${section}"]`);
if (element) {
const offsetTop = element.offsetTop;
const offsetHeight = element.offsetHeight;
if (
scrollPosition >= offsetTop &&
scrollPosition < offsetTop + offsetHeight
) {
if (scrollPosition >= offsetTop && scrollPosition < offsetTop + offsetHeight) {
setActiveSection(section);
break;
}
@ -70,7 +61,6 @@ const Navbar = () => {
return () => window.removeEventListener("scroll", handleScroll);
}, []);
// Update document direction when language changes
useEffect(() => {
if (i18n.language === "ar") {
document.documentElement.dir = "rtl";
@ -82,88 +72,147 @@ const Navbar = () => {
}, [i18n.language]);
const navItems = [
{ key: "home", label: t("nav.home") },
{ key: "services", label: t("nav.services") },
{ key: "about", label: t("nav.about") },
{ key: "contact", label: t("nav.contact") },
{ key: "home", label: t("nav.home", "الرئيسية") },
{ key: "services", label: t("nav.services", "الخدمات") },
{ key: "about", label: t("nav.about", "من نحن") },
{ key: "sections", label: t("nav.sections", "الأقسام") },
{ key: "contact", label: t("nav.contact", "تواصل معنا") },
];
const toggleLang = () => {
const newLang = i18n.language === "ar" ? "en" : "ar";
i18n.changeLanguage(newLang);
};
return (
<nav className="bg-white/10 backdrop-blur-lg fixed top-0 w-full z-50 shadow h-14">
<div className="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto py-2 px-4">
{/* الشعار */}
<img
src="src/assets/TPS-logo.png"
className="h-8 sm:h-10 md:h-12 transition-all duration-300"
alt="TPS Logo"
/>
<nav className="fixed top-6 left-0 right-0 z-50 pointer-events-auto">
<div className="max-w-screen-xl mx-auto px-4">
{/* grid: spacer | centered nav | controls */}
<div className="grid grid-cols-3 items-center">
{/* left: spacer (removed TPS image as requested) */}
<div className="flex items-center">
<div style={{ width: 44 }} />
</div>
{/* الجانب الأيمن */}
<div className="flex items-center md:order-2 space-x-1 md:space-x-0 rtl:space-x-reverse relative">
{/* center: 3D nav card */}
<div className="flex justify-center">
<div
className="hidden md:flex items-center nav-center"
style={{ perspective: "1000px" }}
>
<div className="nav-card bg-gradient-to-r from-white/90 to-gray-100/60 dark:from-gray-800/75 dark:to-gray-900/70 backdrop-blur-sm rounded-2xl px-5 py-3 shadow-2xl">
<ul className="flex items-center space-x-6 rtl:space-x-reverse">
{navItems.map((item) => (
<li key={item.key}>
<Link
to={item.key}
smooth
duration={500}
spy={true}
offset={-70}
onSetActive={() => setActiveSection(item.key)}
onClick={() => {
setActiveSection(item.key);
setMenuOpen(false);
}}
className={`nav-item relative inline-flex items-center text-base md:text-lg font-semibold px-6 py-3 rounded-full transition-all duration-300 focus:outline-none select-none
${
activeSection === item.key
? "text-yellow-500"
: "text-gray-800 dark:text-white"
}
`}
role="link"
aria-current={activeSection === item.key ? "page" : undefined}
>
{/* glow layer */}
<span className="nav-item-glow" aria-hidden="true" />
<span className="relative z-10">{item.label}</span>
</Link>
</li>
))}
</ul>
</div>
</div>
</div>
{/* right: controls (dark mode, language toggle, mobile) */}
<div className="flex justify-end items-center space-x-2 rtl:space-x-reverse">
<button
onClick={toggleDarkMode}
type="button"
className="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 ml-2 transition-colors duration-200"
aria-label={isDarkMode ? "Light Mode" : "Dark Mode"}
title={isDarkMode ? "Light Mode" : "Dark Mode"}
>
{isDarkMode ? (
<svg className="w-5 h-5 text-yellow-500" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" clipRule="evenodd" />
</svg>
) : (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
</svg>
)}
</button>
onClick={toggleDarkMode}
type="button"
className="inline-flex items-center p-2 w-11 h-11 justify-center text-sm text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors duration-200"
aria-label={isDarkMode ? "Light Mode" : "Dark Mode"}
title={isDarkMode ? "Light Mode" : "Dark Mode"}
>
{isDarkMode ? (
<svg className="w-5 h-5 text-yellow-500" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" clipRule="evenodd" />
</svg>
) : (
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
</svg>
)}
</button>
{/* Language Switcher */}
<LanguageSwitcher />
{/* Language toggle (only EN / AR) with a globe + letters icon */}
<button
onClick={toggleLang}
type="button"
className="inline-flex items-center px-3 py-2 rounded-lg border border-transparent hover:border-gray-200 dark:hover:border-gray-700 transition-all duration-200 text-sm font-medium"
aria-label="Toggle language"
title={i18n.language === "ar" ? "العربية" : "English"}
>
<span className="flex items-center gap-2">
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="1.5" />
<path d="M2 12h20M12 2v20M4.5 6.5c3 2.5 6 2.5 15 0" stroke="currentColor" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round" />
</svg>
<span className="text-sm font-semibold">{i18n.language === "ar" ? "ع" : "EN"}</span>
</span>
</button>
{/* زر القائمة للجوال */}
<button
onClick={toggleMenu}
type="button"
className="inline-flex items-center right--30 p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700 ml-2"
aria-controls="navbar-menu"
aria-expanded={menuOpen}
>
<span className="sr-only">Open main menu</span>
<svg className="w-5 h-5" viewBox="0 0 17 14" fill="none">
<path
d="M1 1h15M1 7h15M1 13h15"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
{/* mobile menu toggle */}
<button
onClick={toggleMenu}
type="button"
className="inline-flex items-center p-2 w-11 h-11 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700"
aria-controls="navbar-menu"
aria-expanded={menuOpen}
>
<span className="sr-only">Open main menu</span>
<svg className="w-6 h-6" viewBox="0 0 17 14" fill="none">
<path d="M1 1h15M1 7h15M1 13h15" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</button>
</div>
</div>
{/* روابط الأقسام */}
<div
className={`w-full md:flex md:w-auto md:order-1 transition-all duration-300 ease-in-out ${
menuOpen ? "block" : "hidden"
}`}
id="navbar-menu"
>
<ul className="flex flex-col md:flex-row bg-white dark:bg-gray-900 md:bg-transparent md:dark:bg-transparent p-4 md:p-0 rounded-lg shadow-md md:shadow-none space-y-2 md:space-y-0 md:space-x-6 mt-4 md:mt-0 divide-y divide-gray-200 md:divide-y-0">
{/* Mobile menu (centered items in column) */}
<div className={`${menuOpen ? "block" : "hidden"} md:hidden mt-3 bg-white/90 dark:bg-gray-900/80 backdrop-blur-sm rounded-lg p-3`}>
<ul className="flex flex-col items-center space-y-3">
{navItems.map((item) => (
<li key={item.key} className="pt-2 md:pt-0">
<li key={item.key} className="w-full">
<Link
to={item.key}
smooth
duration={500}
spy={true}
offset={0}
offset={-70}
onSetActive={() => setActiveSection(item.key)}
className={`block text-center md:inline cursor-pointer text-lg font-semibold transition duration-200 ${
activeSection === item.key
? "text-yellow-500"
: "text-gray-800 dark:text-white hover:text-yellow-500"
}`}
onClick={() => {
setActiveSection(item.key);
setMenuOpen(false);
}}
className={`block w-full text-center px-6 py-3 rounded-lg font-semibold transition-all duration-250
${
activeSection === item.key
? "text-yellow-500 ring-4 ring-yellow-300/30 shadow-lg"
: "text-gray-800 dark:text-white hover:text-yellow-500"
}
`}
>
{item.label}
</Link>
@ -172,6 +221,34 @@ const Navbar = () => {
</ul>
</div>
</div>
{/* inline CSS for 3D / hover visual polish */}
<style>{`
.nav-center { perspective: 1000px; }
.nav-card { transform-style: preserve-3d; transition: transform 0.35s cubic-bezier(.2,.9,.3,1), box-shadow 0.35s; }
.nav-item { transform-style: preserve-3d; transition: transform 0.28s, box-shadow 0.28s, color 0.2s; position: relative; }
.nav-item:hover { transform: translateY(-8px) scale(1.05); box-shadow: 0 20px 34px rgba(2,6,23,0.14); }
.nav-item:active { transform: translateY(-4px) scale(1.03); }
.nav-item-glow { position: absolute; inset: -8px; border-radius: 9999px; filter: blur(14px); opacity:0; transition: opacity 0.35s, transform 0.35s; pointer-events:none; z-index:0; }
.nav-item .relative { z-index: 10; }
.nav-item-glow { background: radial-gradient(circle at 50% 40%, rgba(250,204,21,0.14), transparent 30%); transform: scale(0.98); }
.nav-item:hover .nav-item-glow { opacity:0.9; transform: scale(1.03); }
/* IMPORTANT: active (aria-current) NO longer elevates - only color change and subtle underline */
.nav-item[aria-current="page"] { transform: none !important; box-shadow: none !important; }
.nav-item[aria-current="page"]::after { content: ""; position: absolute; height: 3px; left: 18%; right: 18%; bottom: -6px; border-radius: 9999px; background: linear-gradient(90deg, rgba(250,204,21,0.95), rgba(250,200,40,0.9)); opacity: 1; }
/* ensure text doesn't wrap */
.nav-item span { white-space: nowrap; }
/* small accessibility tweak: focus visible style */
.nav-item:focus-visible { outline: 3px solid rgba(99,102,241,0.12); outline-offset: 4px; }
/* mobile: slightly larger tappable targets */
@media (max-width: 767px) {
.nav-item { padding-left: 1rem; padding-right: 1rem; }
}
`}</style>
</nav>
);
};