started contact page, added resume page and pdf rendering, and default state for the skill cards on mobile.
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
export default function About() {
|
||||
return <p>TODO: About()</p>;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react";
|
||||
import VerticalSlider from "./components/VerticalSlider";
|
||||
import Primary from "./Primary";
|
||||
import Projects from "./Projects";
|
||||
import Contact from "./Contact";
|
||||
import "./main.css";
|
||||
import { terminal } from "virtual:terminal";
|
||||
|
||||
@@ -81,6 +82,7 @@ export default function App() {
|
||||
skills={skillsData}
|
||||
/>,
|
||||
<Projects cardData={projectData} />,
|
||||
<Contact />,
|
||||
];
|
||||
|
||||
return <VerticalSlider slides={slides} />;
|
||||
|
||||
+60
-2
@@ -1,5 +1,63 @@
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
/**
|
||||
* TODO: Add email validation function.
|
||||
*/
|
||||
export default function Contact() {
|
||||
return <p>TODO: Contact()</p>;
|
||||
const [formData, setFormData] = useState({
|
||||
name: "",
|
||||
email: "",
|
||||
message: "",
|
||||
});
|
||||
|
||||
const handleChange = (event) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
[event.target.name]: event.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
// Handle form submission here, e.g., send data to server
|
||||
console.log(formData);
|
||||
};
|
||||
|
||||
return (
|
||||
<div id="contact-section">
|
||||
<h1>Contact Me</h1>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="row">
|
||||
<input
|
||||
placeholder="Name"
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<input
|
||||
placeholder="example@email.com"
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<textarea
|
||||
placeholder="Message"
|
||||
id="message"
|
||||
name="message"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="row">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+6
-12
@@ -1,16 +1,8 @@
|
||||
// TODO: Replace h4 callToAction with a GREEN button callToAction.
|
||||
import React, { useState } from "react";
|
||||
import SkillCard from "./components/SkillCard";
|
||||
|
||||
export default function Primary({
|
||||
primaryText,
|
||||
secondaryText,
|
||||
callToAction,
|
||||
image,
|
||||
skills,
|
||||
}) {
|
||||
const [activeCardIndex, setActiveCardIndex] = useState(null);
|
||||
() => setActiveCardIndex(0);
|
||||
export default function Primary({ primaryText, secondaryText, image, skills }) {
|
||||
const [activeCardIndex, setActiveCardIndex] = useState(0);
|
||||
return (
|
||||
<>
|
||||
<div id="primary">
|
||||
@@ -18,13 +10,15 @@ export default function Primary({
|
||||
<div id="header-text">
|
||||
<h1>{primaryText}</h1>
|
||||
<h3>{secondaryText}</h3>
|
||||
<h4>{callToAction}</h4>
|
||||
<a className="button" href="/resume" target="_blank">
|
||||
<div className="button-grid">Access my resume</div>
|
||||
</a>
|
||||
</div>
|
||||
<div id="header-image">
|
||||
<img id="profile" src={image} />
|
||||
</div>
|
||||
</div>
|
||||
<div id="skills-cards">
|
||||
<div id="skill-cards">
|
||||
<SkillCard
|
||||
header="Languages"
|
||||
symbol="code"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from "react";
|
||||
import PdfViewer from "./components/PdfViewer";
|
||||
|
||||
export default function Resume() {
|
||||
return (
|
||||
<>
|
||||
<PdfViewer file="resume.pdf" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
export default function Skills({ skills }) {
|
||||
const languages = skills.languages;
|
||||
const tools = skills.tools;
|
||||
const other = skills.other;
|
||||
return <></>;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React, { createContext, useState } from "react";
|
||||
|
||||
const AccordionContext = createContext();
|
||||
|
||||
export const AccordionProvider = ({ children }) => {
|
||||
const [expandedCardId, setExpandedCardId] = useState(null);
|
||||
|
||||
const handleAccordionClick = (cardId) => {
|
||||
setExpandedCardId(cardId === expandedCardId ? null : cardId);
|
||||
};
|
||||
|
||||
return (
|
||||
<AccordionContext.Provider value={{ expandedCardId, handleAccordionClick }}>
|
||||
{children}
|
||||
</AccordionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccordionContext;
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from "react";
|
||||
import { Document, Page, pdfjs } from "react-pdf";
|
||||
import "react-pdf/dist/Page/TextLayer.css";
|
||||
import "react-pdf/dist/Page/AnnotationLayer.css";
|
||||
|
||||
export default function PdfViewer({ file }) {
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||
"pdfjs-dist/build/pdf.worker.min.mjs",
|
||||
import.meta.url,
|
||||
).toString();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="pdf-container">
|
||||
<Document file={file}>
|
||||
<Page pageNumber={1} />
|
||||
<Page pageNumber={2} />
|
||||
</Document>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,23 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
// TODO: Add state which only allows one skill card at a time to be expanded.
|
||||
// TODO: Ensure that the expandability of the skill card is only an option when @media max-width 790px (mobile)
|
||||
const SkillCard = ({ header, symbol, list, isActive, onToggle }) => {
|
||||
// FUNCTION DATA
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth <= 790);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
const mediaQuery = window.matchMedia("(max-width: 790px)");
|
||||
setIsMobile(mediaQuery.matches);
|
||||
};
|
||||
|
||||
handleResize(); // Initial check on mount
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="card shadow">
|
||||
<div className="skill-card-header" onClick={() => onToggle()}>
|
||||
|
||||
+52
-5
@@ -87,22 +87,31 @@ h4 {
|
||||
|
||||
/**SWIPER PAGES**/
|
||||
.swiper {
|
||||
width: 100%;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.slide-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mySwiper {
|
||||
width: 100vw;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/**ACTIVE PAGE ANIMATIONS**/
|
||||
#header,
|
||||
#skill-cards,
|
||||
#project-section,
|
||||
#project-cards-section {
|
||||
opacity: 0;
|
||||
}
|
||||
.swiper-slide-active #header,
|
||||
.swiper-slide-active #skill-cards,
|
||||
.swiper-slide-active #project-section,
|
||||
@@ -159,7 +168,7 @@ h4 {
|
||||
overflow: hidden;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 60% 35%;
|
||||
grid-template-columns: 55% 45%;
|
||||
grid-template-rows: 100%;
|
||||
column-gap: 2vw;
|
||||
}
|
||||
@@ -199,7 +208,7 @@ h4 {
|
||||
animation: imgMoveDown 0.8s ease-in forwards 0.2s;
|
||||
}
|
||||
|
||||
#skills-cards {
|
||||
#skill-cards {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
@@ -264,6 +273,11 @@ h4 {
|
||||
grid-template-rows: 20% 80%;
|
||||
}
|
||||
|
||||
#project-grid-container h1 {
|
||||
margin-left: 5vw;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#project-cards-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -347,8 +361,7 @@ h4 {
|
||||
}
|
||||
|
||||
.button {
|
||||
height: 100%;
|
||||
width: 50%;
|
||||
width: 80%;
|
||||
color: #4a245e;
|
||||
background-color: #ebd2ff;
|
||||
border-radius: 18px;
|
||||
@@ -359,6 +372,31 @@ h4 {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1vw;
|
||||
}
|
||||
|
||||
/**Contact Section**/
|
||||
#contact-section {
|
||||
height: 90vh;
|
||||
width: 100%;
|
||||
margin: 5vw;
|
||||
}
|
||||
|
||||
#contact-section h1 {
|
||||
margin-left: 5vw;
|
||||
}
|
||||
|
||||
#contact-section form {
|
||||
border: 1px solid white;
|
||||
height: 70vh;
|
||||
}
|
||||
|
||||
/**PDF Viewer**/
|
||||
.pdf-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/**Animations**/
|
||||
@@ -524,6 +562,15 @@ h4 {
|
||||
.project-text-column {
|
||||
margin-left: 5vw;
|
||||
}
|
||||
|
||||
.pdf-viewer {
|
||||
max-width: 100%;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.pdf-page {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 485px) {
|
||||
|
||||
+13
-3
@@ -1,6 +1,16 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { BrowserRouter as Router, Routes, Route } from "react-router";
|
||||
import App from "./App.jsx";
|
||||
import Resume from "./Resume.jsx";
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById("app"));
|
||||
const container = document.getElementById("app");
|
||||
const root = createRoot(container); // createRoot(container!) if you use TypeScript
|
||||
root.render(
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<App />} />
|
||||
<Route path="/resume" element={<Resume />} />
|
||||
</Routes>
|
||||
</Router>,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user