fixed the navbar and edited the themes config and edited the home page and enhancced the canves background joints and lines with more shapes
All checks were successful
Build frontend / build (push) Successful in 37s
All checks were successful
Build frontend / build (push) Successful in 37s
This commit is contained in:
80
src/App.jsx
80
src/App.jsx
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom";
|
import { BrowserRouter, Routes, Route, useLocation } from "react-router-dom";
|
||||||
import { AnimatePresence, LayoutGroup } from "framer-motion";
|
import { AnimatePresence, LayoutGroup } from "framer-motion";
|
||||||
@ -25,7 +24,6 @@ import DepartmentDetail7 from "./Components/Sections/DepartmentDetail7/Departmen
|
|||||||
import DepartmentDetail8 from "./Components/Sections/DepartmentDetail8/DepartmentDetail8";
|
import DepartmentDetail8 from "./Components/Sections/DepartmentDetail8/DepartmentDetail8";
|
||||||
import DepartmentDetail9 from "./Components/Sections/DepartmentDetail9/DepartmentDetail9";
|
import DepartmentDetail9 from "./Components/Sections/DepartmentDetail9/DepartmentDetail9";
|
||||||
|
|
||||||
|
|
||||||
const MainPage = ({ theme }) => {
|
const MainPage = ({ theme }) => {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-transparent">
|
<div className="min-h-screen bg-transparent">
|
||||||
@ -49,8 +47,8 @@ function RouterView({ theme, toggleTheme }) {
|
|||||||
<LayoutGroup>
|
<LayoutGroup>
|
||||||
<AnimatePresence mode="wait" initial={false}>
|
<AnimatePresence mode="wait" initial={false}>
|
||||||
<Routes location={location} key={location.pathname}>
|
<Routes location={location} key={location.pathname}>
|
||||||
<Route
|
<Route
|
||||||
path="/"
|
path="/"
|
||||||
element={
|
element={
|
||||||
<div className="relative min-h-screen bg-transparent">
|
<div className="relative min-h-screen bg-transparent">
|
||||||
<BackgroundCanvas theme={theme} />
|
<BackgroundCanvas theme={theme} />
|
||||||
@ -58,19 +56,18 @@ function RouterView({ theme, toggleTheme }) {
|
|||||||
<MainPage theme={theme} />
|
<MainPage theme={theme} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route path="/departments/:id" element={<DepartmentDetail />} />
|
<Route path="/departments/:id" element={<DepartmentDetail />} />
|
||||||
<Route path="/department-detail2" element={<DepartmentDetail2 />} />
|
<Route path="/department-detail2" element={<DepartmentDetail2 />} />
|
||||||
<Route path="/department-detail3" element={<DepartmentDetail3 />} />
|
<Route path="/department-detail3" element={<DepartmentDetail3 />} />
|
||||||
<Route path="/department-detail4" element={<DepartmentDetail4 />} />
|
<Route path="/department-detail4" element={<DepartmentDetail4 />} />
|
||||||
<Route path="/department-detail5" element={<DepartmentDetail5/>} />
|
<Route path="/department-detail5" element={<DepartmentDetail5 />} />
|
||||||
<Route path="/department-detail6" element={<DepartmentDetail6/>} />
|
<Route path="/department-detail6" element={<DepartmentDetail6 />} />
|
||||||
<Route path="/department-detail7" element={<DepartmentDetail7/>} />
|
<Route path="/department-detail7" element={<DepartmentDetail7 />} />
|
||||||
|
|
||||||
<Route path="/department-detail8" element={<DepartmentDetail8/>} />
|
|
||||||
<Route path="/department-detail9" element={<DepartmentDetail9/>} />
|
|
||||||
|
|
||||||
|
<Route path="/department-detail8" element={<DepartmentDetail8 />} />
|
||||||
|
<Route path="/department-detail9" element={<DepartmentDetail9 />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</LayoutGroup>
|
</LayoutGroup>
|
||||||
@ -82,19 +79,21 @@ function Layout({ theme, toggleTheme }) {
|
|||||||
|
|
||||||
const excludedExactPaths = [
|
const excludedExactPaths = [
|
||||||
"/department-detail2",
|
"/department-detail2",
|
||||||
"/department-detail3" ,
|
"/department-detail3",
|
||||||
"/department-detail4" ,
|
"/department-detail4",
|
||||||
"/department-detail5" ,
|
"/department-detail5",
|
||||||
"/department-detail6" ,
|
"/department-detail6",
|
||||||
"/department-detail7",
|
"/department-detail7",
|
||||||
"/department-detail8",
|
"/department-detail8",
|
||||||
"/department-detail9"
|
"/department-detail9",
|
||||||
];
|
];
|
||||||
|
|
||||||
const excludedPrefixes = ["/departments/"];
|
const excludedPrefixes = ["/departments/"];
|
||||||
|
|
||||||
const isExcludedExact = excludedExactPaths.includes(location.pathname);
|
const isExcludedExact = excludedExactPaths.includes(location.pathname);
|
||||||
const isExcludedPrefix = excludedPrefixes.some((p) => location.pathname.startsWith(p));
|
const isExcludedPrefix = excludedPrefixes.some((p) =>
|
||||||
|
location.pathname.startsWith(p),
|
||||||
|
);
|
||||||
|
|
||||||
const hideNavbar = isExcludedExact || isExcludedPrefix;
|
const hideNavbar = isExcludedExact || isExcludedPrefix;
|
||||||
|
|
||||||
@ -106,7 +105,7 @@ function Layout({ theme, toggleTheme }) {
|
|||||||
|
|
||||||
<div style={{ paddingTop: navbarHeight }}>
|
<div style={{ paddingTop: navbarHeight }}>
|
||||||
<RouterView theme={theme} toggleTheme={toggleTheme} />
|
<RouterView theme={theme} toggleTheme={toggleTheme} />
|
||||||
|
|
||||||
{location.pathname === "/" && <Footer />}
|
{location.pathname === "/" && <Footer />}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@ -114,13 +113,16 @@ function Layout({ theme, toggleTheme }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [theme, setTheme] = useState("light");
|
const [theme, setTheme] = useState("dark");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("Current theme:", theme);
|
console.log("Current theme:", theme);
|
||||||
console.log("HTML has dark class:", document.documentElement.classList.contains('dark'));
|
console.log(
|
||||||
|
"HTML has dark class:",
|
||||||
const canvas = document.querySelector('.background-canvas');
|
document.documentElement.classList.contains("dark"),
|
||||||
|
);
|
||||||
|
|
||||||
|
const canvas = document.querySelector(".background-canvas");
|
||||||
if (canvas) {
|
if (canvas) {
|
||||||
console.log("Canvas found:", canvas);
|
console.log("Canvas found:", canvas);
|
||||||
console.log("Canvas dimensions:", canvas.width, "x", canvas.height);
|
console.log("Canvas dimensions:", canvas.width, "x", canvas.height);
|
||||||
@ -131,35 +133,37 @@ const App = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const savedTheme = localStorage.getItem("theme");
|
const savedTheme = localStorage.getItem("theme");
|
||||||
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
const prefersDark = window.matchMedia(
|
||||||
|
"(prefers-color-scheme: dark)",
|
||||||
if (savedTheme === "dark" || (!savedTheme && prefersDark)) {
|
).matches;
|
||||||
|
|
||||||
|
if (savedTheme === "light") {
|
||||||
|
setTheme("light");
|
||||||
|
document.documentElement.classList.remove("dark");
|
||||||
|
} else {
|
||||||
setTheme("dark");
|
setTheme("dark");
|
||||||
document.documentElement.classList.add("dark");
|
document.documentElement.classList.add("dark");
|
||||||
} else {
|
|
||||||
document.documentElement.classList.remove("dark");
|
|
||||||
setTheme("light");
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toggleTheme = () => {
|
const toggleTheme = () => {
|
||||||
const newTheme = theme === "light" ? "dark" : "light";
|
const newTheme = theme === "light" ? "dark" : "light";
|
||||||
setTheme(newTheme);
|
setTheme(newTheme);
|
||||||
|
|
||||||
if (newTheme === "dark") {
|
if (newTheme === "dark") {
|
||||||
document.documentElement.classList.add("dark");
|
document.documentElement.classList.add("dark");
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.remove("dark");
|
document.documentElement.classList.remove("dark");
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.setItem("theme", newTheme);
|
localStorage.setItem("theme", newTheme);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Layout theme={theme} toggleTheme={toggleTheme} />
|
<Layout theme={theme} toggleTheme={toggleTheme} />
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|||||||
@ -1,121 +1,125 @@
|
|||||||
html {
|
html {
|
||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
min-height: 100% !important;
|
min-height: 100% !important;
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
overflow-y: auto !important;
|
overflow-y: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.background-canvas {
|
.background-canvas {
|
||||||
position: fixed !important;
|
position: fixed !important;
|
||||||
top: 0 !important;
|
top: 0 !important;
|
||||||
left: 0 !important;
|
left: 0 !important;
|
||||||
width: 100vw !important;
|
width: 100vw !important;
|
||||||
height: 100vh !important;
|
height: 100vh !important; /* Fallback */
|
||||||
z-index: -1 !important;
|
height: 100dvh !important; /* Dynamic Viewport Height for mobile */
|
||||||
pointer-events: none !important;
|
z-index: -1 !important;
|
||||||
overflow: hidden !important;
|
pointer-events: none !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
background-color: var(
|
||||||
|
--bg-color
|
||||||
|
); /* Ensure background color is consistent even if canvas lags */
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 0px !important;
|
width: 0px !important;
|
||||||
height: 0px !important;
|
height: 0px !important;
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
border-radius: 0px !important;
|
border-radius: 0px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
scrollbar-width: none !important;
|
scrollbar-width: none !important;
|
||||||
scrollbar-color: transparent transparent !important;
|
scrollbar-color: transparent transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
-ms-overflow-style: -ms-autohiding-scrollbar !important;
|
-ms-overflow-style: -ms-autohiding-scrollbar !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
z-index: 10 !important;
|
z-index: 10 !important;
|
||||||
min-height: 100vh !important;
|
min-height: 100vh !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
/* padding: 20px !important; */
|
/* padding: 20px !important; */
|
||||||
}
|
}
|
||||||
|
|
||||||
nav.fixed {
|
nav.fixed {
|
||||||
z-index: 50 !important;
|
z-index: 50 !important;
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.background-canvas {
|
.background-canvas {
|
||||||
opacity: 0.8 !important;
|
opacity: 0.8 !important;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
-webkit-overflow-scrolling: touch !important;
|
-webkit-overflow-scrolling: touch !important;
|
||||||
overscroll-behavior-y: contain !important;
|
overscroll-behavior-y: contain !important;
|
||||||
}
|
}
|
||||||
* {
|
* {
|
||||||
-webkit-tap-highlight-color: transparent !important;
|
-webkit-tap-highlight-color: transparent !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-container {
|
.content-container {
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
z-index: 10 !important;
|
z-index: 10 !important;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
overflow-y: auto !important;
|
overflow-y: auto !important;
|
||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
-webkit-overflow-scrolling: touch !important;
|
-webkit-overflow-scrolling: touch !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-container::-webkit-scrollbar {
|
.scroll-container::-webkit-scrollbar {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-container:hover::-webkit-scrollbar {
|
.scroll-container:hover::-webkit-scrollbar {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@supports (-webkit-touch-callout: none) {
|
@supports (-webkit-touch-callout: none) {
|
||||||
body {
|
body {
|
||||||
height: -webkit-fill-available !important;
|
height: -webkit-fill-available !important;
|
||||||
}
|
}
|
||||||
.background-canvas {
|
.background-canvas {
|
||||||
height: -webkit-fill-available !important;
|
height: -webkit-fill-available !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-on-scroll {
|
.fade-on-scroll {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-on-scroll.hidden {
|
.fade-on-scroll.hidden {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import React, { useRef, useEffect, useState } from 'react';
|
import React, { useRef, useEffect, useState } from "react";
|
||||||
import './BackgroundCanvas.css';
|
import "./BackgroundCanvas.css";
|
||||||
|
|
||||||
const BackgroundCanvas = ({ theme = 'light' }) => {
|
const BackgroundCanvas = ({ theme = "light" }) => {
|
||||||
const canvasRef = useRef(null);
|
const canvasRef = useRef(null);
|
||||||
const animationRef = useRef(null);
|
const animationRef = useRef(null);
|
||||||
const jointsRef = useRef([]);
|
const jointsRef = useRef([]);
|
||||||
const cameraRef = useRef(null);
|
const cameraRef = useRef(null);
|
||||||
const keysRef = useRef(new Array(127).fill(0));
|
const keysRef = useRef(new Array(127).fill(0));
|
||||||
const worldRef = useRef({ width: 0, height: 0 });
|
const worldRef = useRef({ width: 0, height: 0 });
|
||||||
const themeRef = useRef(theme);
|
const themeRef = useRef(theme);
|
||||||
|
|
||||||
class Camera {
|
class Camera {
|
||||||
constructor(position, zoom) {
|
constructor(position, zoom) {
|
||||||
@ -23,10 +23,14 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
|
|||||||
constructor(position, vector) {
|
constructor(position, vector) {
|
||||||
this.position = position;
|
this.position = position;
|
||||||
this.vector = vector;
|
this.vector = vector;
|
||||||
this.speed = 0.5;
|
this.speed = Math.random() * 1.5 + 0.5;
|
||||||
this.w = 2;
|
|
||||||
this.h = 2;
|
|
||||||
this.bone_length = 150;
|
this.bone_length = 150;
|
||||||
|
const shapes = ["circle", "triangle", "square", "pentagon"];
|
||||||
|
this.shape = shapes[Math.floor(Math.random() * shapes.length)];
|
||||||
|
this.size = Math.random() * 4 + 3;
|
||||||
|
this.angle = 0;
|
||||||
|
this.angularSpeed = (Math.random() - 0.5) * 0.1;
|
||||||
|
this.pulse = Math.random() * Math.PI * 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +45,7 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
|
|||||||
{
|
{
|
||||||
x: Math.random() * 2 - 1,
|
x: Math.random() * 2 - 1,
|
||||||
y: Math.random() * 2 - 1,
|
y: Math.random() * 2 - 1,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return arr;
|
return arr;
|
||||||
@ -52,6 +56,9 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
|
|||||||
joint.position.x += joint.vector.x * joint.speed;
|
joint.position.x += joint.vector.x * joint.speed;
|
||||||
joint.position.y += joint.vector.y * joint.speed;
|
joint.position.y += joint.vector.y * joint.speed;
|
||||||
|
|
||||||
|
joint.angle += joint.angularSpeed;
|
||||||
|
joint.pulse += 0.05;
|
||||||
|
|
||||||
if (joint.position.x < 0) joint.position.x = world.width;
|
if (joint.position.x < 0) joint.position.x = world.width;
|
||||||
if (joint.position.x > world.width) joint.position.x = 0;
|
if (joint.position.x > world.width) joint.position.x = 0;
|
||||||
if (joint.position.y < 0) joint.position.y = world.height;
|
if (joint.position.y < 0) joint.position.y = world.height;
|
||||||
@ -61,62 +68,94 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
|
|||||||
|
|
||||||
const drawJoints = (ctx, joints, camera, view, theme) => {
|
const drawJoints = (ctx, joints, camera, view, theme) => {
|
||||||
const len = joints.length;
|
const len = joints.length;
|
||||||
|
|
||||||
let lineColor, pointColor, backgroundColor;
|
let lineColor, pointColor, backgroundColor;
|
||||||
if (theme === 'dark') {
|
if (theme === "dark") {
|
||||||
backgroundColor = '#446a85';
|
backgroundColor = "#313131";
|
||||||
lineColor = '#FFFFFF';
|
lineColor = "#F5EEE6";
|
||||||
pointColor = '#F5EEE6';
|
pointColor = "#e06923";
|
||||||
} else {
|
} else {
|
||||||
backgroundColor = '#d3dde3';
|
backgroundColor = "#f1f1f1ff";
|
||||||
lineColor = '#131313';
|
lineColor = "rgba(49, 49, 49, 0.3)"; // More subtle lines
|
||||||
pointColor = '#041c40';
|
pointColor = "#041c40";
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.fillStyle = backgroundColor;
|
ctx.fillStyle = backgroundColor;
|
||||||
ctx.fillRect(0, 0, view.width, view.height);
|
ctx.fillRect(0, 0, view.width, view.height);
|
||||||
|
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
for (let j = i + 1; j < len; j += 3) {
|
for (let j = i + 1; j < len; j += 3) {
|
||||||
const length = Math.hypot(
|
const length = Math.hypot(
|
||||||
joints[j].position.x - joints[i].position.x,
|
joints[j].position.x - joints[i].position.x,
|
||||||
joints[j].position.y - joints[i].position.y
|
joints[j].position.y - joints[i].position.y,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (length <= joints[i].bone_length) {
|
if (length <= joints[i].bone_length) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.strokeStyle = lineColor;
|
ctx.strokeStyle = lineColor;
|
||||||
ctx.lineWidth = camera.zoom * (30 / length);
|
ctx.lineWidth = camera.zoom * (30 / length);
|
||||||
ctx.moveTo(
|
ctx.moveTo(
|
||||||
view.width / 2 + (joints[i].position.x - camera.position.x) * camera.zoom,
|
view.width / 2 +
|
||||||
view.height / 2 + (joints[i].position.y - camera.position.y) * camera.zoom
|
(joints[i].position.x - camera.position.x) * camera.zoom,
|
||||||
|
view.height / 2 +
|
||||||
|
(joints[i].position.y - camera.position.y) * camera.zoom,
|
||||||
);
|
);
|
||||||
ctx.lineTo(
|
ctx.lineTo(
|
||||||
view.width / 2 + (joints[j].position.x - camera.position.x) * camera.zoom,
|
view.width / 2 +
|
||||||
view.height / 2 + (joints[j].position.y - camera.position.y) * camera.zoom
|
(joints[j].position.x - camera.position.x) * camera.zoom,
|
||||||
|
view.height / 2 +
|
||||||
|
(joints[j].position.y - camera.position.y) * camera.zoom,
|
||||||
);
|
);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.fillStyle = pointColor;
|
const currentSize =
|
||||||
ctx.fillRect(
|
(joints[i].size + Math.sin(joints[i].pulse) * 1.5) * camera.zoom;
|
||||||
view.width / 2 + ((joints[i].position.x - camera.position.x) - joints[i].w / 2) * camera.zoom,
|
|
||||||
view.height / 2 + ((joints[i].position.y - camera.position.y) - joints[i].w / 2) * camera.zoom,
|
ctx.save();
|
||||||
joints[i].w * camera.zoom,
|
ctx.translate(
|
||||||
joints[i].h * camera.zoom
|
view.width / 2 +
|
||||||
|
(joints[i].position.x - camera.position.x) * camera.zoom,
|
||||||
|
view.height / 2 +
|
||||||
|
(joints[i].position.y - camera.position.y) * camera.zoom,
|
||||||
);
|
);
|
||||||
|
ctx.rotate(joints[i].angle);
|
||||||
|
|
||||||
|
ctx.fillStyle = pointColor;
|
||||||
|
ctx.beginPath();
|
||||||
|
|
||||||
|
if (joints[i].shape === "circle") {
|
||||||
|
ctx.arc(0, 0, currentSize, 0, Math.PI * 2);
|
||||||
|
} else if (joints[i].shape === "triangle") {
|
||||||
|
ctx.moveTo(0, -currentSize);
|
||||||
|
ctx.lineTo(currentSize, currentSize);
|
||||||
|
ctx.lineTo(-currentSize, currentSize);
|
||||||
|
} else if (joints[i].shape === "square") {
|
||||||
|
ctx.rect(-currentSize / 2, -currentSize / 2, currentSize, currentSize);
|
||||||
|
} else if (joints[i].shape === "pentagon") {
|
||||||
|
for (let k = 0; k < 5; k++) {
|
||||||
|
ctx.lineTo(
|
||||||
|
currentSize * Math.cos((k * 2 * Math.PI) / 5 - Math.PI / 2),
|
||||||
|
currentSize * Math.sin((k * 2 * Math.PI) / 5 - Math.PI / 2),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fill();
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.restore();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const moveCamera = (camera, keys) => {
|
const moveCamera = (camera, keys) => {
|
||||||
if (keys[37]) camera.acceleration.x -= camera.speed;
|
if (keys[37]) camera.acceleration.x -= camera.speed;
|
||||||
if (keys[38]) camera.acceleration.y -= camera.speed;
|
if (keys[38]) camera.acceleration.y -= camera.speed;
|
||||||
if (keys[39]) camera.acceleration.x += camera.speed;
|
if (keys[39]) camera.acceleration.x += camera.speed;
|
||||||
if (keys[40]) camera.acceleration.y += camera.speed;
|
if (keys[40]) camera.acceleration.y += camera.speed;
|
||||||
if (keys[188]) camera.acceleration.z += 0.003;
|
if (keys[188]) camera.acceleration.z += 0.003;
|
||||||
if (keys[190]) camera.acceleration.z -= 0.003;
|
if (keys[190]) camera.acceleration.z -= 0.003;
|
||||||
|
|
||||||
camera.position.x += camera.acceleration.x;
|
camera.position.x += camera.acceleration.x;
|
||||||
camera.position.y += camera.acceleration.y;
|
camera.position.y += camera.acceleration.y;
|
||||||
@ -127,49 +166,53 @@ const BackgroundCanvas = ({ theme = 'light' }) => {
|
|||||||
camera.acceleration.z *= 0.9;
|
camera.acceleration.z *= 0.9;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initCanvas = () => {
|
const initCanvas = () => {
|
||||||
const canvas = canvasRef.current;
|
const canvas = canvasRef.current;
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
|
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext("2d");
|
||||||
const view = {
|
const view = {
|
||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
height: window.innerHeight,
|
height: window.innerHeight,
|
||||||
|
};
|
||||||
|
|
||||||
|
canvas.width = view.width;
|
||||||
|
canvas.height = view.height;
|
||||||
|
|
||||||
|
worldRef.current = {
|
||||||
|
width: view.width * 2,
|
||||||
|
height: view.height * 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
cameraRef.current = new Camera(
|
||||||
|
{
|
||||||
|
x: worldRef.current.width / 2,
|
||||||
|
y: worldRef.current.height / 2,
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isMobile = window.innerWidth < 768;
|
||||||
|
const numberOfJoints = isMobile ? 150 : 600;
|
||||||
|
jointsRef.current = generateJoints(
|
||||||
|
numberOfJoints,
|
||||||
|
worldRef.current.width,
|
||||||
|
worldRef.current.height,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
jointsRef.current.forEach((joint) => {
|
||||||
|
joint.bone_length = 120;
|
||||||
|
joint.speed = 0.4;
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
canvas.width = view.width;
|
|
||||||
canvas.height = view.height;
|
|
||||||
|
|
||||||
worldRef.current = {
|
|
||||||
width: view.width * 2,
|
|
||||||
height: view.height * 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
cameraRef.current = new Camera(
|
|
||||||
{
|
|
||||||
x: worldRef.current.width / 2,
|
|
||||||
y: worldRef.current.height / 2,
|
|
||||||
},
|
|
||||||
1
|
|
||||||
);
|
|
||||||
|
|
||||||
const isMobile = window.innerWidth < 768;
|
|
||||||
const numberOfJoints = isMobile ? 80 : 300;
|
|
||||||
jointsRef.current = generateJoints(numberOfJoints, worldRef.current.width, worldRef.current.height);
|
|
||||||
|
|
||||||
if (isMobile) {
|
|
||||||
jointsRef.current.forEach(joint => {
|
|
||||||
joint.bone_length = 120;
|
|
||||||
joint.speed = 0.4;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const animate = () => {
|
const animate = () => {
|
||||||
const canvas = canvasRef.current;
|
const canvas = canvasRef.current;
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
|
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext("2d");
|
||||||
const view = {
|
const view = {
|
||||||
width: canvas.width,
|
width: canvas.width,
|
||||||
height: canvas.height,
|
height: canvas.height,
|
||||||
@ -177,8 +220,14 @@ const initCanvas = () => {
|
|||||||
|
|
||||||
moveCamera(cameraRef.current, keysRef.current);
|
moveCamera(cameraRef.current, keysRef.current);
|
||||||
moveJoints(jointsRef.current, worldRef.current);
|
moveJoints(jointsRef.current, worldRef.current);
|
||||||
|
|
||||||
drawJoints(ctx, jointsRef.current, cameraRef.current, view, themeRef.current);
|
drawJoints(
|
||||||
|
ctx,
|
||||||
|
jointsRef.current,
|
||||||
|
cameraRef.current,
|
||||||
|
view,
|
||||||
|
themeRef.current,
|
||||||
|
);
|
||||||
|
|
||||||
animationRef.current = requestAnimationFrame(animate);
|
animationRef.current = requestAnimationFrame(animate);
|
||||||
};
|
};
|
||||||
@ -196,12 +245,12 @@ const initCanvas = () => {
|
|||||||
keysRef.current[e.keyCode] = 0;
|
keysRef.current[e.keyCode] = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('keydown', handleKeyDown);
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
window.addEventListener('keyup', handleKeyUp);
|
window.addEventListener("keyup", handleKeyUp);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('keydown', handleKeyDown);
|
window.removeEventListener("keydown", handleKeyDown);
|
||||||
window.removeEventListener('keyup', handleKeyUp);
|
window.removeEventListener("keyup", handleKeyUp);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -219,8 +268,8 @@ const initCanvas = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('resize', handleResize);
|
window.addEventListener("resize", handleResize);
|
||||||
return () => window.removeEventListener('resize', handleResize);
|
return () => window.removeEventListener("resize", handleResize);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -232,15 +281,11 @@ const initCanvas = () => {
|
|||||||
cancelAnimationFrame(animationRef.current);
|
cancelAnimationFrame(animationRef.current);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<canvas
|
<canvas ref={canvasRef} className="background-canvas" aria-hidden="true" />
|
||||||
ref={canvasRef}
|
|
||||||
className="background-canvas"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default BackgroundCanvas;
|
export default BackgroundCanvas;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const StyledWrapper = styled.div`
|
|||||||
.theme-toggle-btn {
|
.theme-toggle-btn {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
color: ${props => props.theme === 'dark' ? '#FFD700' : '#FF8C00'};
|
color: ${(props) => (props.theme === "dark" ? "#FFD700" : "#FF8C00")};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@ -18,12 +18,15 @@ const StyledWrapper = styled.div`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: ${props => props.theme === 'dark' ? 'rgba(255, 215, 0, 0.1)' : 'rgba(255, 140, 0, 0.1)'};
|
background: ${(props) =>
|
||||||
|
props.theme === "dark"
|
||||||
|
? "rgba(255, 215, 0, 0.1)"
|
||||||
|
: "rgba(255, 140, 0, 0.1)"};
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
@ -46,10 +49,11 @@ const ThemeToggle = ({ currentTheme, toggleTheme }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const LanguageSwitcher = ({ i18n }) => {
|
const LanguageSwitcher = ({ i18n, currentTheme }) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const current = i18n.language?.startsWith("ar") ? "ar" : "en";
|
const current = i18n.language?.startsWith("ar") ? "ar" : "en";
|
||||||
|
const isDark = currentTheme === "dark";
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onDocClick = (e) => {
|
const onDocClick = (e) => {
|
||||||
@ -66,7 +70,14 @@ const LanguageSwitcher = ({ i18n }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref} style={{ position: "relative", display: "inline-flex", alignItems: "center" }}>
|
<div
|
||||||
|
ref={ref}
|
||||||
|
style={{
|
||||||
|
position: "relative",
|
||||||
|
display: "inline-flex",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
@ -77,7 +88,7 @@ const LanguageSwitcher = ({ i18n }) => {
|
|||||||
borderRadius: "50%",
|
borderRadius: "50%",
|
||||||
border: "none",
|
border: "none",
|
||||||
background: "transparent",
|
background: "transparent",
|
||||||
color: "inherit",
|
color: isDark ? "#e6e6e6" : "#313131",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
display: "inline-flex",
|
display: "inline-flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
@ -98,12 +109,15 @@ const LanguageSwitcher = ({ i18n }) => {
|
|||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: "calc(100% + 8px)",
|
top: "calc(100% + 8px)",
|
||||||
right: 0,
|
right: 0,
|
||||||
background: "linear-gradient(180deg,#111827,#0b1220)",
|
background: isDark
|
||||||
|
? "linear-gradient(180deg, #446a85, #2d4b5f)"
|
||||||
|
: "#ffffff",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
boxShadow: "0 8px 30px rgba(2,6,23,0.5)",
|
boxShadow: "0 8px 30px rgba(2,6,23,0.5)",
|
||||||
padding: "6px",
|
padding: "6px",
|
||||||
zIndex: 60,
|
zIndex: 60,
|
||||||
minWidth: 96,
|
minWidth: 96,
|
||||||
|
border: isDark ? "none" : "1px solid #e5e7eb",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@ -117,8 +131,13 @@ const LanguageSwitcher = ({ i18n }) => {
|
|||||||
textAlign: "left",
|
textAlign: "left",
|
||||||
padding: "8px 10px",
|
padding: "8px 10px",
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
background: current === "en" ? "rgba(255,183,77,0.12)" : "transparent",
|
background:
|
||||||
color: "inherit",
|
current === "en"
|
||||||
|
? isDark
|
||||||
|
? "rgba(255,183,77,0.12)"
|
||||||
|
: "#f3f4f6"
|
||||||
|
: "transparent",
|
||||||
|
color: isDark ? "inherit" : "#313131",
|
||||||
border: "none",
|
border: "none",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
@ -139,8 +158,13 @@ const LanguageSwitcher = ({ i18n }) => {
|
|||||||
textAlign: "left",
|
textAlign: "left",
|
||||||
padding: "8px 10px",
|
padding: "8px 10px",
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
background: current === "ar" ? "rgba(255,183,77,0.12)" : "transparent",
|
background:
|
||||||
color: "inherit",
|
current === "ar"
|
||||||
|
? isDark
|
||||||
|
? "rgba(255,183,77,0.12)"
|
||||||
|
: "#f3f4f6"
|
||||||
|
: "transparent",
|
||||||
|
color: isDark ? "inherit" : "#313131",
|
||||||
border: "none",
|
border: "none",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
@ -169,11 +193,16 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
|
|||||||
const scrollPosition = window.scrollY + 140;
|
const scrollPosition = window.scrollY + 140;
|
||||||
|
|
||||||
for (const section of sections) {
|
for (const section of sections) {
|
||||||
const element = document.getElementById(section) || document.querySelector(`[name="${section}"]`);
|
const element =
|
||||||
|
document.getElementById(section) ||
|
||||||
|
document.querySelector(`[name="${section}"]`);
|
||||||
if (element) {
|
if (element) {
|
||||||
const offsetTop = element.offsetTop;
|
const offsetTop = element.offsetTop;
|
||||||
const offsetHeight = element.offsetHeight;
|
const offsetHeight = element.offsetHeight;
|
||||||
if (scrollPosition >= offsetTop && scrollPosition < offsetTop + offsetHeight) {
|
if (
|
||||||
|
scrollPosition >= offsetTop &&
|
||||||
|
scrollPosition < offsetTop + offsetHeight
|
||||||
|
) {
|
||||||
setActiveSection(section);
|
setActiveSection(section);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -224,11 +253,13 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.glass-nav {
|
.glass-nav {
|
||||||
background: linear-gradient(180deg, rgba(75,85,99,0.96), rgba(55,65,81,0.9));
|
background: rgba(227, 232, 236, 0.9);
|
||||||
color: #e6e6e6;
|
color: #313131;
|
||||||
border-bottom: 1px solid rgba(255,255,255,0.05);
|
border-bottom: 1px solid rgba(4, 28, 64, 0.1);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(12px);
|
||||||
-webkit-backdrop-filter: blur(8px);
|
-webkit-backdrop-filter: blur(12px);
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
@ -260,35 +291,35 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
|
|||||||
.nav-link:hover .nav-pill {
|
.nav-link:hover .nav-pill {
|
||||||
transform: translateY(-6px) scale(1.06);
|
transform: translateY(-6px) scale(1.06);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 14px 40px rgba(2,6,23,0.28),
|
0 14px 40px rgba(224, 105, 35, 0.28),
|
||||||
0 6px 20px rgba(255,122,24,0.08);
|
0 6px 20px rgba(224, 105, 35, 0.08);
|
||||||
background: linear-gradient(90deg, #ffd27a, #ff8a00);
|
background: #e06923;
|
||||||
color: #050505;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link.active .nav-pill,
|
.nav-link.active .nav-pill,
|
||||||
.mobile-drawer .nav-pill.active {
|
.mobile-drawer .nav-pill.active {
|
||||||
background: radial-gradient(circle at 30% 30%, #ffb347, #ff7a18) !important;
|
background: #e06923 !important;
|
||||||
color: #0b0b0b !important;
|
color: #ffffff !important;
|
||||||
transform: translateY(-3px) scale(1.03);
|
transform: translateY(-3px) scale(1.03);
|
||||||
box-shadow: 0 8px 26px rgba(255, 122, 24, 0.16), 0 2px 6px rgba(0,0,0,0.12);
|
box-shadow: 0 8px 26px rgba(224, 105, 35, 0.25), 0 2px 6px rgba(0,0,0,0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link:active .nav-pill,
|
.nav-link:active .nav-pill,
|
||||||
.mobile-drawer a:active .nav-pill {
|
.mobile-drawer a:active .nav-pill {
|
||||||
transition-duration: 0s !important;
|
transition-duration: 0s !important;
|
||||||
transform: translateY(-3px) scale(1.03) !important;
|
transform: translateY(-3px) scale(1.03) !important;
|
||||||
background: radial-gradient(circle at 30% 30%, #ffb347, #ff7a18) !important;
|
background: #e06923 !important;
|
||||||
color: #0b0b0b !important;
|
color: #ffffff !important;
|
||||||
box-shadow: 0 8px 26px rgba(255, 122, 24, 0.16) !important;
|
box-shadow: 0 8px 26px rgba(224, 105, 35, 0.25) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link:focus-visible .nav-pill,
|
.nav-link:focus-visible .nav-pill,
|
||||||
.mobile-drawer a:focus-visible .nav-pill {
|
.mobile-drawer a:focus-visible .nav-pill {
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: 0 0 0 6px rgba(255,165,0,0.14), 0 8px 30px rgba(255,122,24,0.12);
|
box-shadow: 0 0 0 6px rgba(224, 105, 35, 0.14), 0 8px 30px rgba(224, 105, 35, 0.12);
|
||||||
background: radial-gradient(circle at 30% 30%, #ffb347, #ff7a18);
|
background: #e06923;
|
||||||
color: #0b0b0b;
|
color: #ffffff;
|
||||||
transform: translateY(-3px) scale(1.02);
|
transform: translateY(-3px) scale(1.02);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,7 +376,11 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
|
|||||||
<div className="mx-auto max-w-screen-xl">
|
<div className="mx-auto max-w-screen-xl">
|
||||||
<div className="flex items-center justify-between h-14 px-2 md:px-4">
|
<div className="flex items-center justify-between h-14 px-2 md:px-4">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div aria-hidden className="logo-placeholder" style={{ width: 36, height: 36 }} />
|
<div
|
||||||
|
aria-hidden
|
||||||
|
className="logo-placeholder"
|
||||||
|
style={{ width: 36, height: 36 }}
|
||||||
|
/>
|
||||||
<div className="hidden md:flex items-center space-x-6 rtl:space-x-reverse ml-6">
|
<div className="hidden md:flex items-center space-x-6 rtl:space-x-reverse ml-6">
|
||||||
<ul className="flex items-center gap-6">
|
<ul className="flex items-center gap-6">
|
||||||
{navItems.map((item) => {
|
{navItems.map((item) => {
|
||||||
@ -361,13 +396,25 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
|
|||||||
onSetActive={() => setActiveSection(item.key)}
|
onSetActive={() => setActiveSection(item.key)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveSection(item.key);
|
setActiveSection(item.key);
|
||||||
if (item.key === "home") scroll.scrollToTop({ duration: 600 });
|
if (item.key === "home")
|
||||||
|
scroll.scrollToTop({ duration: 600 });
|
||||||
if (menuOpen) setMenuOpen(false);
|
if (menuOpen) setMenuOpen(false);
|
||||||
}}
|
}}
|
||||||
className={`nav-link cursor-pointer text-sm md:text-lg ${isActive ? "active" : "text-slate-200 dark:text-slate-200/90"}`}
|
className={`nav-link cursor-pointer text-sm md:text-lg ${isActive ? "active" : ""}`}
|
||||||
|
style={{
|
||||||
|
color: isActive
|
||||||
|
? ""
|
||||||
|
: currentTheme === "dark"
|
||||||
|
? "rgba(229,231,235,0.95)"
|
||||||
|
: "#313131",
|
||||||
|
}}
|
||||||
aria-current={isActive ? "page" : undefined}
|
aria-current={isActive ? "page" : undefined}
|
||||||
>
|
>
|
||||||
<span className={`nav-pill ${isActive ? "active" : ""}`}>{item.label}</span>
|
<span
|
||||||
|
className={`nav-pill ${isActive ? "active" : ""}`}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
@ -378,13 +425,22 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
|
|||||||
|
|
||||||
<div className="flex items-center gap-3 md:gap-5">
|
<div className="flex items-center gap-3 md:gap-5">
|
||||||
<div className="hidden md:flex items-center gap-3">
|
<div className="hidden md:flex items-center gap-3">
|
||||||
<LanguageSwitcher i18n={i18n} />
|
<LanguageSwitcher i18n={i18n} currentTheme={currentTheme} />
|
||||||
<ThemeToggle currentTheme={currentTheme} toggleTheme={toggleTheme} />
|
<ThemeToggle
|
||||||
|
currentTheme={currentTheme}
|
||||||
|
toggleTheme={toggleTheme}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="md:hidden w-full">
|
<div className="md:hidden w-full">
|
||||||
{!isRtl ? (
|
{!isRtl ? (
|
||||||
<div style={{ display: "flex", alignItems: "center", width: "100%" }}>
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div style={{ display: "flex", alignItems: "center" }}>
|
<div style={{ display: "flex", alignItems: "center" }}>
|
||||||
<button
|
<button
|
||||||
onClick={() => setMenuOpen((s) => !s)}
|
onClick={() => setMenuOpen((s) => !s)}
|
||||||
@ -394,23 +450,56 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
|
|||||||
style={{ marginRight: 8 }}
|
style={{ marginRight: 8 }}
|
||||||
>
|
>
|
||||||
<span className="sr-only">Open main menu</span>
|
<span className="sr-only">Open main menu</span>
|
||||||
<svg className="h-6 w-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
|
<svg
|
||||||
{menuOpen ? <path d="M6 18L18 6M6 6l12 12" /> : <><path d="M3 6h18" /><path d="M3 12h18" /><path d="M3 18h18" /></>}
|
className="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
aria-hidden
|
||||||
|
>
|
||||||
|
{menuOpen ? (
|
||||||
|
<path d="M6 18L18 6M6 6l12 12" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<path d="M3 6h18" />
|
||||||
|
<path d="M3 12h18" />
|
||||||
|
<path d="M3 18h18" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ flex: 1 }} />
|
<div style={{ flex: 1 }} />
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
<div
|
||||||
<ThemeToggle currentTheme={currentTheme} toggleTheme={toggleTheme} />
|
style={{ display: "flex", alignItems: "center", gap: 8 }}
|
||||||
|
>
|
||||||
|
<ThemeToggle
|
||||||
|
currentTheme={currentTheme}
|
||||||
|
toggleTheme={toggleTheme}
|
||||||
|
/>
|
||||||
<LanguageSwitcher i18n={i18n} />
|
<LanguageSwitcher i18n={i18n} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ display: "flex", alignItems: "center", width: "100%" }}>
|
<div
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
style={{
|
||||||
<ThemeToggle currentTheme={currentTheme} toggleTheme={toggleTheme} />
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{ display: "flex", alignItems: "center", gap: 8 }}
|
||||||
|
>
|
||||||
|
<ThemeToggle
|
||||||
|
currentTheme={currentTheme}
|
||||||
|
toggleTheme={toggleTheme}
|
||||||
|
/>
|
||||||
<LanguageSwitcher i18n={i18n} />
|
<LanguageSwitcher i18n={i18n} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -425,8 +514,25 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
|
|||||||
style={{ marginLeft: 8 }}
|
style={{ marginLeft: 8 }}
|
||||||
>
|
>
|
||||||
<span className="sr-only">Open main menu</span>
|
<span className="sr-only">Open main menu</span>
|
||||||
<svg className="h-6 w-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
|
<svg
|
||||||
{menuOpen ? <path d="M6 18L18 6M6 6l12 12" /> : <><path d="M3 6h18" /><path d="M3 12h18" /><path d="M3 18h18" /></>}
|
className="h-6 w-6"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
aria-hidden
|
||||||
|
>
|
||||||
|
{menuOpen ? (
|
||||||
|
<path d="M6 18L18 6M6 6l12 12" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<path d="M3 6h18" />
|
||||||
|
<path d="M3 12h18" />
|
||||||
|
<path d="M3 18h18" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -454,14 +560,19 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
|
|||||||
offset={-110}
|
offset={-110}
|
||||||
onSetActive={() => setActiveSection(item.key)}
|
onSetActive={() => setActiveSection(item.key)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveSection(item.key);
|
setActiveSection(item.key);
|
||||||
if (item.key === "home") scroll.scrollToTop({ duration: 600 });
|
if (item.key === "home")
|
||||||
|
scroll.scrollToTop({ duration: 600 });
|
||||||
setMenuOpen(false);
|
setMenuOpen(false);
|
||||||
}}
|
}}
|
||||||
className={`block w-full text-right px-3 py-2 rounded-md font-semibold text-base ${isActive ? "text-amber-400" : "text-slate-200 hover:text-amber-400"}`}
|
className={`block w-full text-right px-3 py-2 rounded-md font-semibold text-base ${isActive ? "text-amber-400" : "text-[#313131] dark:text-slate-200 hover:text-amber-400"}`}
|
||||||
aria-current={isActive ? "page" : undefined}
|
aria-current={isActive ? "page" : undefined}
|
||||||
>
|
>
|
||||||
<span className={`nav-pill ${isActive ? "active" : ""}`}>{item.label}</span>
|
<span
|
||||||
|
className={`nav-pill ${isActive ? "active" : ""}`}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
@ -475,4 +586,4 @@ const Navbar = ({ toggleTheme, currentTheme }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Navbar;
|
export default Navbar;
|
||||||
|
|||||||
@ -14,11 +14,13 @@ export default function EngineeringHeroFlowbite() {
|
|||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
|
|
||||||
const defaultConfig = {
|
const defaultConfig = {
|
||||||
main_title: "عندما تطلب الرؤية مستشارًا، ويحتاج المخطط منفذًا استراتيجيًا…\nنكون القرار بالقيادة من الرؤية حتى التسليم",
|
main_title:
|
||||||
|
"عندما تطلب الرؤية مستشارًا، ويحتاج المخطط منفذًا استراتيجيًا…\nنكون القرار بالقيادة من الرؤية حتى التسليم",
|
||||||
subtitle:
|
subtitle:
|
||||||
"حلول متكاملة تشمل التصميم، التنفيذ، التشغيل، والصيانة\nللمشاريع الصناعية والمدنية، وفق أحدث المعايير والتقنيات",
|
"حلول متكاملة تشمل التصميم، التنفيذ، التشغيل، والصيانة\nللمشاريع الصناعية والمدنية، وفق أحدث المعايير والتقنيات",
|
||||||
primary_color: "#e67e22",
|
primary_color: "#e67e22",
|
||||||
background_color: "#000000",
|
// background_color: "transparent",
|
||||||
|
background_color: "#7c2a2aff",
|
||||||
text_color: "#ffffff",
|
text_color: "#ffffff",
|
||||||
secondary_surface: "#95a5a6",
|
secondary_surface: "#95a5a6",
|
||||||
secondary_action: "#34495e",
|
secondary_action: "#34495e",
|
||||||
@ -91,10 +93,10 @@ export default function EngineeringHeroFlowbite() {
|
|||||||
width <= 400
|
width <= 400
|
||||||
? baseSize * 0.78
|
? baseSize * 0.78
|
||||||
: width <= 640
|
: width <= 640
|
||||||
? baseSize * 0.88
|
? baseSize * 0.88
|
||||||
: width <= 1024
|
: width <= 1024
|
||||||
? baseSize * 0.96
|
? baseSize * 0.96
|
||||||
: baseSize;
|
: baseSize;
|
||||||
const headingMultiplier = width <= 640 ? 2.6 : 3.2;
|
const headingMultiplier = width <= 640 ? 2.6 : 3.2;
|
||||||
const subtitleMultiplier = width <= 640 ? 0.95 : 1.1;
|
const subtitleMultiplier = width <= 640 ? 0.95 : 1.1;
|
||||||
const mainText = (config.main_title || defaultConfig.main_title)
|
const mainText = (config.main_title || defaultConfig.main_title)
|
||||||
@ -106,6 +108,7 @@ export default function EngineeringHeroFlowbite() {
|
|||||||
const maxFont = 68;
|
const maxFont = 68;
|
||||||
const minFont = Math.max(Math.round(responsiveBase * 1.6), 14);
|
const minFont = Math.max(Math.round(responsiveBase * 1.6), 14);
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
|
const isDark = root.classList.contains("dark");
|
||||||
root.style.setProperty("--base", `${responsiveBase}px`);
|
root.style.setProperty("--base", `${responsiveBase}px`);
|
||||||
|
|
||||||
if (main) {
|
if (main) {
|
||||||
@ -126,12 +129,14 @@ export default function EngineeringHeroFlowbite() {
|
|||||||
const computedSize = Math.min(
|
const computedSize = Math.min(
|
||||||
Math.max(
|
Math.max(
|
||||||
Math.round(responsiveBase * headingMultiplier * lengthFactor),
|
Math.round(responsiveBase * headingMultiplier * lengthFactor),
|
||||||
minFont
|
minFont,
|
||||||
),
|
),
|
||||||
maxFont
|
maxFont,
|
||||||
);
|
);
|
||||||
main.style.fontSize = `${computedSize}px`;
|
main.style.fontSize = `${computedSize}px`;
|
||||||
main.style.color = config.text_color || defaultConfig.text_color;
|
main.style.color = isDark
|
||||||
|
? config.text_color || defaultConfig.text_color
|
||||||
|
: config.light_text_color || defaultConfig.light_text_color;
|
||||||
main.style.fontWeight = 800;
|
main.style.fontWeight = 800;
|
||||||
main.style.textAlign = isArabic ? "right" : "left";
|
main.style.textAlign = isArabic ? "right" : "left";
|
||||||
main.style.whiteSpace = "normal";
|
main.style.whiteSpace = "normal";
|
||||||
@ -139,6 +144,7 @@ export default function EngineeringHeroFlowbite() {
|
|||||||
main.style.overflowWrap = "anywhere";
|
main.style.overflowWrap = "anywhere";
|
||||||
main.style.direction = isArabic ? "rtl" : "ltr";
|
main.style.direction = isArabic ? "rtl" : "ltr";
|
||||||
main.style.display = "block";
|
main.style.display = "block";
|
||||||
|
main.style.textShadow = isDark ? "0 4px 10px rgba(0,0,0,0.5)" : "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sub) {
|
if (sub) {
|
||||||
@ -149,7 +155,9 @@ export default function EngineeringHeroFlowbite() {
|
|||||||
sub.style.fontFamily = font;
|
sub.style.fontFamily = font;
|
||||||
const subSize = Math.round(responsiveBase * subtitleMultiplier);
|
const subSize = Math.round(responsiveBase * subtitleMultiplier);
|
||||||
sub.style.fontSize = `${subSize}px`;
|
sub.style.fontSize = `${subSize}px`;
|
||||||
sub.style.color = config.text_color || defaultConfig.text_color;
|
sub.style.color = isDark
|
||||||
|
? config.text_color || defaultConfig.text_color
|
||||||
|
: config.light_text_color || defaultConfig.light_text_color;
|
||||||
sub.style.textAlign = isArabic ? "right" : "left";
|
sub.style.textAlign = isArabic ? "right" : "left";
|
||||||
sub.style.maxWidth = "800px";
|
sub.style.maxWidth = "800px";
|
||||||
sub.style.whiteSpace = "normal";
|
sub.style.whiteSpace = "normal";
|
||||||
@ -161,19 +169,19 @@ export default function EngineeringHeroFlowbite() {
|
|||||||
|
|
||||||
root.style.setProperty(
|
root.style.setProperty(
|
||||||
"--ehb-primary",
|
"--ehb-primary",
|
||||||
config.primary_color || defaultConfig.primary_color
|
config.primary_color || defaultConfig.primary_color,
|
||||||
);
|
);
|
||||||
root.style.setProperty(
|
root.style.setProperty(
|
||||||
"--ehb-background",
|
"--ehb-background",
|
||||||
config.background_color || defaultConfig.background_color
|
config.background_color || defaultConfig.background_color,
|
||||||
);
|
);
|
||||||
root.style.setProperty(
|
root.style.setProperty(
|
||||||
"--ehb-surface",
|
"--ehb-surface",
|
||||||
config.secondary_surface || defaultConfig.secondary_surface
|
config.secondary_surface || defaultConfig.secondary_surface,
|
||||||
);
|
);
|
||||||
root.style.setProperty(
|
root.style.setProperty(
|
||||||
"--ehb-action",
|
"--ehb-action",
|
||||||
config.secondary_action || defaultConfig.secondary_action
|
config.secondary_action || defaultConfig.secondary_action,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -230,7 +238,19 @@ export default function EngineeringHeroFlowbite() {
|
|||||||
:root { --ehb-primary: #e67e22; --ehb-background: #000000; --ehb-surface: #95a5a6; --ehb-action: #34495e; --base: 16px }
|
:root { --ehb-primary: #e67e22; --ehb-background: #000000; --ehb-surface: #95a5a6; --ehb-action: #34495e; --base: 16px }
|
||||||
|
|
||||||
.hero-section{position:relative;width:100%;height:100%;overflow:hidden}
|
.hero-section{position:relative;width:100%;height:100%;overflow:hidden}
|
||||||
.hero-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:linear-gradient(35deg, #57acd900 0%, rgb(151 162 179 / 0%) 50%, #47718b 100%);z-index:3}
|
.hero-overlay{
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
left:0;
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
z-index:3;
|
||||||
|
background: transparent;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
}
|
||||||
|
.dark .hero-overlay {
|
||||||
|
background: linear-gradient(35deg, transparent 0%, transparent 50%, rgba(71, 113, 139, 0.2) 100%);
|
||||||
|
}
|
||||||
.hero-layout{position:relative;z-index:10;height:100%;display:flex;align-items:center;justify-content:space-between;padding:clamp(12px,4vw,40px);gap:2rem;direction:ltr;flex-direction:row}
|
.hero-layout{position:relative;z-index:10;height:100%;display:flex;align-items:center;justify-content:space-between;padding:clamp(12px,4vw,40px);gap:2rem;direction:ltr;flex-direction:row}
|
||||||
.hero-layout.layout-ltr{flex-direction:row-reverse}
|
.hero-layout.layout-ltr{flex-direction:row-reverse}
|
||||||
.hero-left{flex:1;display:flex;align-items:center;justify-content:flex-start;padding:20px;position:relative;flex-direction:column}
|
.hero-left{flex:1;display:flex;align-items:center;justify-content:flex-start;padding:20px;position:relative;flex-direction:column}
|
||||||
|
|||||||
@ -1,40 +1,47 @@
|
|||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700;800&display=swap');
|
@import url("https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700;800&display=swap");
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
overflow-y: scroll !important;
|
overflow-y: scroll !important;
|
||||||
--primary: #041c40;
|
--primary: #041c40;
|
||||||
--secondary: #e06923;
|
--secondary: #e06923;
|
||||||
--tertiary: #313131;
|
--tertiary: #313131;
|
||||||
--bg-color: #F5EEE6;
|
--bg-color: #e3e8ec;
|
||||||
--text-color: #313131;
|
--text-color: #313131;
|
||||||
--border-color: #d1c9be;
|
--border-color: #d1c9be;
|
||||||
}
|
}
|
||||||
.dark {
|
.dark {
|
||||||
overflow-y: scroll !important;
|
overflow-y: scroll !important;
|
||||||
--primary: #041c40;
|
--primary: #041c40;
|
||||||
--secondary: #e06923;
|
--secondary: #e06923;
|
||||||
--tertiary: #313131;
|
--tertiary: #313131;
|
||||||
--bg-color: #313131;
|
--bg-color: #313131;
|
||||||
--text-color: #F5EEE6;
|
--text-color: #f5eee6;
|
||||||
--border-color: #4a4a4a;
|
--border-color: #4a4a4a;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
overflow-y: scroll !important;
|
overflow-y: scroll !important;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
font-family: 'Cairo', system-ui, -apple-system, sans-serif;
|
font-family:
|
||||||
color: var(--text-color);
|
"Cairo",
|
||||||
transition: background-color 0.3s ease, color 0.3s ease;
|
system-ui,
|
||||||
}
|
-apple-system,
|
||||||
html {
|
sans-serif;
|
||||||
overflow-y: scroll !important;
|
color: var(--text-color);
|
||||||
scroll-behavior: smooth;
|
background-color: var(--bg-color);
|
||||||
overflow-x: hidden;
|
transition:
|
||||||
}
|
background-color 0.3s ease,
|
||||||
}
|
color 0.3s ease;
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
overflow-y: scroll !important;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
20
src/main.jsx
20
src/main.jsx
@ -1,15 +1,15 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from "react-dom/client";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
import './index.css';
|
import "./index.css";
|
||||||
import './App.css';
|
import "./App.css";
|
||||||
|
|
||||||
document.documentElement.style.setProperty('--bg-color', '#F5EEE6');
|
document.documentElement.style.setProperty("--bg-color", "#F5EEE6");
|
||||||
document.documentElement.style.setProperty('--text-color', '#131313');
|
document.documentElement.style.setProperty("--text-color", "#131313");
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,25 +1,22 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
darkMode: 'class',
|
darkMode: "class",
|
||||||
content: [
|
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
"./index.html",
|
theme: {
|
||||||
"./src/**/*.{js,ts,jsx,tsx}",
|
extend: {
|
||||||
],
|
colors: {
|
||||||
theme: {
|
primary: "#041c40",
|
||||||
extend: {
|
secondary: "#e06923",
|
||||||
colors: {
|
"dark-bg": "#313131",
|
||||||
primary: '#041c40',
|
"light-bg": "#E3E8EC",
|
||||||
secondary: '#e06923',
|
},
|
||||||
'dark-bg': '#313131',
|
backdropBlur: {
|
||||||
'light-bg': '#F5EEE6',
|
xs: "2px",
|
||||||
},
|
sm: "4px",
|
||||||
backdropBlur: {
|
md: "8px",
|
||||||
'xs': '2px',
|
lg: "12px",
|
||||||
'sm': '4px',
|
xl: "20px",
|
||||||
'md': '8px',
|
},
|
||||||
'lg': '12px',
|
|
||||||
'xl': '20px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
plugins: [],
|
},
|
||||||
}
|
plugins: [],
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user