Added Skills
This commit is contained in:
Generated
+43
@@ -8,6 +8,7 @@
|
|||||||
"name": "portfolio",
|
"name": "portfolio",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"framer-motion": "^12.6.2",
|
||||||
"next": "15.2.0",
|
"next": "15.2.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0"
|
||||||
@@ -2745,6 +2746,33 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "12.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.6.2.tgz",
|
||||||
|
"integrity": "sha512-7LgPRlPs5aG8UxeZiMCMZz8firC53+2+9TnWV22tuSi38D3IFRxHRUqOREKckAkt6ztX+Dn6weLcatQilJTMcg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-dom": "^12.6.1",
|
||||||
|
"motion-utils": "^12.5.0",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
@@ -3955,6 +3983,21 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/motion-dom": {
|
||||||
|
"version": "12.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.6.1.tgz",
|
||||||
|
"integrity": "sha512-8XVsriTUEVOepoIDgE/LDGdg7qaKXWdt+wQA/8z0p8YzJDLYL8gbimZ3YkCLlj7bB2i/4UBD/g+VO7y9ZY0zHQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-utils": "^12.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-utils": {
|
||||||
|
"version": "12.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.5.0.tgz",
|
||||||
|
"integrity": "sha512-+hFFzvimn0sBMP9iPxBa9OtRX35ZQ3py0UHnb8U29VD+d8lQ8zH3dTygJWqK7av2v6yhg7scj9iZuvTS0f4+SA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
|||||||
+5
-4
@@ -9,15 +9,16 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"framer-motion": "^12.6.2",
|
||||||
|
"next": "15.2.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0"
|
||||||
"next": "15.2.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^3",
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
"tailwindcss": "^4",
|
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.2.0",
|
"eslint-config-next": "15.2.0",
|
||||||
"@eslint/eslintrc": "^3"
|
"tailwindcss": "^4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-58
@@ -9,34 +9,42 @@ export default function About() {
|
|||||||
content: "I discovered my passion for technology by experimenting with Scratch and creating some simple Games."
|
content: "I discovered my passion for technology by experimenting with Scratch and creating some simple Games."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: "2022",
|
date: "2020",
|
||||||
title: "Exploring Web Development",
|
title: "Exploring Web & Scripting",
|
||||||
content: "Started creating my first website and got into HTML and CSS."
|
content: "Started creating small websites and got into JavaScript, CSS, and working with various scripting languages."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: "2023",
|
date: "2022",
|
||||||
title: "Diving into Informatics",
|
title: "Diving into Game Development",
|
||||||
content: "Started with Python and basic exploration of 'real' programming."
|
content: "Tried Unity and C#, building small 2D and 3D prototypes to expand my skill set."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
date: "2024",
|
date: "2024",
|
||||||
title: "Full Stack & Beyond",
|
title: "Full Stack & Beyond",
|
||||||
content: "Ventured into backend (SQL, Python) and frontend using Javascript"
|
content: "Ventured into backend (SQL, Python) and continued refining my frontend abilities, always exploring new challenges."
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const skills = [
|
||||||
|
{ name: "JavaScript", level: 80, waveSpeed: "2s" },
|
||||||
|
{ name: "Python", level: 75, waveSpeed: "2.5s" },
|
||||||
|
{ name: "C#", level: 50, waveSpeed: "3s" },
|
||||||
|
{ name: "SQL", level: 85, waveSpeed: "2.2s" },
|
||||||
|
{ name: "HTML & CSS", level: 70, waveSpeed: "2.7s" },
|
||||||
|
];
|
||||||
|
|
||||||
const sectionRefs = timelineData.map(() => useRef(null));
|
const sectionRefs = timelineData.map(() => useRef(null));
|
||||||
const aboutMeRef = useRef(null);
|
const aboutMeRef = useRef(null);
|
||||||
const [currentSection, setCurrentSection] = useState(0);
|
|
||||||
const [timelineFinished, setTimelineFinished] = useState(false);
|
|
||||||
const [isManualScroll, setIsManualScroll] = useState(false); // Track user scrolling
|
|
||||||
const scrollContainerRef = useRef(null);
|
const scrollContainerRef = useRef(null);
|
||||||
|
|
||||||
|
const [currentSection, setCurrentSection] = useState(0);
|
||||||
|
const [timelineFinished, setTimelineFinished] = useState(false);
|
||||||
|
|
||||||
|
// Scroll to 'About Me' title and reset timeline on page load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentSection(0); // Reset timeline
|
setCurrentSection(0); // Reset timeline
|
||||||
setTimelineFinished(false);
|
setTimelineFinished(false);
|
||||||
|
|
||||||
// Ensure 'About Me' is visible on reload
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
aboutMeRef.current?.scrollIntoView({behavior: "smooth", block: "start"});
|
aboutMeRef.current?.scrollIntoView({behavior: "smooth", block: "start"});
|
||||||
}, 100);
|
}, 100);
|
||||||
@@ -44,50 +52,33 @@ export default function About() {
|
|||||||
|
|
||||||
// Handle button navigation
|
// Handle button navigation
|
||||||
const handleButtonClick = (direction) => {
|
const handleButtonClick = (direction) => {
|
||||||
setIsManualScroll(false); // Ensure programmatic scrolling happens
|
|
||||||
setCurrentSection((prev) => {
|
setCurrentSection((prev) => {
|
||||||
const newSection = Math.max(0, Math.min(prev + direction, timelineData.length - 1));
|
const newSection = Math.max(0, Math.min(prev + direction, timelineData.length - 1));
|
||||||
if (newSection === timelineData.length - 1) {
|
if (newSection === timelineData.length - 1) {
|
||||||
setTimelineFinished(true);
|
setTimelineFinished(true);
|
||||||
|
} else {
|
||||||
|
setTimelineFinished(false);
|
||||||
}
|
}
|
||||||
|
sectionRefs[newSection]?.current?.scrollIntoView({behavior: "smooth", inline: "start"});
|
||||||
return newSection;
|
return newSection;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Scroll to current section (only if not manually scrolling)
|
// Track scrolling manually and update currentSection
|
||||||
useEffect(() => {
|
|
||||||
if (!isManualScroll) {
|
|
||||||
sectionRefs[currentSection]?.current?.scrollIntoView({behavior: "smooth", inline: "start"});
|
|
||||||
}
|
|
||||||
}, [currentSection, isManualScroll]);
|
|
||||||
useEffect(() => {
|
|
||||||
const handleScroll = () => {
|
|
||||||
setIsManualScroll(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollContainer = scrollContainerRef.current;
|
|
||||||
if (scrollContainer) {
|
|
||||||
scrollContainer.addEventListener("scroll", handleScroll);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (scrollContainer) {
|
|
||||||
scrollContainer.removeEventListener("scroll", handleScroll);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Intersection Observer to detect when last section is fully visible
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
if (entry.isIntersecting && entry.target === sectionRefs[timelineData.length - 1].current) {
|
if (entry.isIntersecting) {
|
||||||
setTimelineFinished(true);
|
const index = sectionRefs.findIndex(ref => ref.current === entry.target);
|
||||||
|
if (index !== -1) {
|
||||||
|
setCurrentSection(index);
|
||||||
|
setTimelineFinished(index === timelineData.length - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{threshold: 0.8} // Increased threshold to ensure full visibility before triggering
|
{threshold: 0.6}
|
||||||
);
|
);
|
||||||
|
|
||||||
sectionRefs.forEach((ref) => {
|
sectionRefs.forEach((ref) => {
|
||||||
@@ -115,7 +106,7 @@ export default function About() {
|
|||||||
<h1 style={{fontSize: "2.5rem", marginBottom: "1rem"}}>About Me</h1>
|
<h1 style={{fontSize: "2.5rem", marginBottom: "1rem"}}>About Me</h1>
|
||||||
<p style={{maxWidth: "600px", textAlign: "center"}}>
|
<p style={{maxWidth: "600px", textAlign: "center"}}>
|
||||||
I’m a passionate developer always excited about new challenges.
|
I’m a passionate developer always excited about new challenges.
|
||||||
Scroll down to check out my coding journey through the years!
|
Scroll down to learn more about me!
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -195,26 +186,31 @@ export default function About() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* Skills Section with Liquid Progress Bars */}
|
||||||
{timelineFinished && (
|
{timelineFinished && (
|
||||||
<section
|
<section style={{ minHeight: "100vh", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "2rem", color: "#fff" }}>
|
||||||
style={{
|
<h2 style={{ fontSize: "2rem", marginBottom: "2rem" }}>My Skills</h2>
|
||||||
minHeight: "100vh",
|
|
||||||
display: "flex",
|
<div style={{ display: "flex", gap: "2rem" }}>
|
||||||
flexDirection: "column",
|
{skills.map((skill, index) => (
|
||||||
alignItems: "center",
|
<div key={index} style={{ textAlign: "center" }}>
|
||||||
justifyContent: "center",
|
<div style={{ width: "60px", height: "200px", backgroundColor: "#333", borderRadius: "10px", overflow: "hidden", position: "relative", border: "2px solid #555" }}>
|
||||||
padding: "2rem",
|
<div className="liquid" style={{
|
||||||
boxSizing: "border-box",
|
width: "100%",
|
||||||
}}
|
height: `${skill.level}%`,
|
||||||
>
|
position: "absolute",
|
||||||
<h2 style={{fontSize: "2rem", marginBottom: "1rem"}}>My Skills</h2>
|
bottom: 0,
|
||||||
<p style={{maxWidth: "600px", textAlign: "center"}}>
|
background: "linear-gradient(45deg, #7400a9, #411357)",
|
||||||
By completing projects in web development, scripting, and game
|
borderTop: "4px solid rgba(255, 255, 255, 0.6)",
|
||||||
design, I’ve cultivated a diverse set of skills — spanning
|
animation: `wave ${skill.waveSpeed} infinite ease-in-out`,
|
||||||
JavaScript, Python, C#, and more!
|
}}><p style={{ marginTop: "10px", fontSize: "1rem" }}>{skill.level}%</p></div>
|
||||||
</p>
|
</div>
|
||||||
|
<p style={{ marginTop: "10px", fontSize: "1rem" }}>{skill.name}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
@@ -181,3 +181,15 @@ body {
|
|||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes wave {
|
||||||
|
0% {
|
||||||
|
transform: translateY(8px);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(0px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(8px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user