fixed bug regarding PDF viewer being cut off, though it isn't resizing for mobile. implemented desktop-only layout of SLCC e-portfolio

This commit is contained in:
Joshua Ashton
2025-01-05 15:15:38 -07:00
parent 3227c4d699
commit 867e7a5706
22 changed files with 572 additions and 105 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react";
import VerticalSlider from "./components/VerticalSlider";
import Primary from "./Primary";
import Home from "./Home";
import Projects from "./Projects";
import Contact from "./Contact";
import "./main.css";
@@ -73,7 +73,7 @@ export default function App() {
};
const slides = [
<Primary
<Home
primaryText="Hi, I'm Josh Ashton."
secondaryText="Junior full-stack developer."
callToAction="Here's my resume."
+58
View File
@@ -0,0 +1,58 @@
import React, { useState } from "react";
import SkillCard from "./components/SkillCard";
export default function Home({ primaryText, secondaryText, image, skills }) {
const [activeCardIndex, setActiveCardIndex] = useState(0);
return (
<>
<div id="primary">
<div className="header">
<div className="header-image">
<img id="profile" src={image} />
</div>
<div className="header-text">
<div className="center-column">
<h1>{primaryText}</h1>
<h3>{secondaryText}</h3>
<a className="button" href="/resume" target="_blank">
<div className="button-grid">Access my resume</div>
</a>
</div>
</div>
</div>
{skills.languages !== null ? (
<div id="skill-cards">
<div id="card-grid">
<SkillCard
header="Languages"
symbol="code"
list={skills.languages}
isActive={activeCardIndex === 0}
onToggle={() => setActiveCardIndex(0)}
/>
<SkillCard
header="Tools"
symbol="terminal"
list={skills.tools}
isActive={activeCardIndex === 1}
onToggle={() => setActiveCardIndex(1)}
/>
<SkillCard
header="Other"
symbol="hub"
list={skills.other}
isActive={activeCardIndex === 2}
onToggle={() => setActiveCardIndex(2)}
/>
</div>
</div>
) : (
<p>Something here</p>
)}
<span className="material-icons down-indicator">
keyboard_double_arrow_down
</span>
</div>
</>
);
}
-52
View File
@@ -1,52 +0,0 @@
import React, { useState } from "react";
import SkillCard from "./components/SkillCard";
export default function Primary({ primaryText, secondaryText, image, skills }) {
const [activeCardIndex, setActiveCardIndex] = useState(0);
return (
<>
<div id="primary">
<div id="header">
<div id="header-text">
<h1>{primaryText}</h1>
<h3>{secondaryText}</h3>
<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="skill-cards">
<div id="card-grid">
<SkillCard
header="Languages"
symbol="code"
list={skills.languages}
isActive={activeCardIndex === 0}
onToggle={() => setActiveCardIndex(0)}
/>
<SkillCard
header="Tools"
symbol="terminal"
list={skills.tools}
isActive={activeCardIndex === 1}
onToggle={() => setActiveCardIndex(1)}
/>
<SkillCard
header="Other"
symbol="hub"
list={skills.other}
isActive={activeCardIndex === 2}
onToggle={() => setActiveCardIndex(2)}
/>
</div>
</div>
<span className="material-icons down-indicator">
keyboard_double_arrow_down
</span>
</div>
</>
);
}
+28
View File
@@ -0,0 +1,28 @@
import React, { useState } from "react";
import SlccCourseView from "./SlccCourseView.jsx";
import SlccNav from "./components/SlccNav.jsx";
import SlccSplash from "./components/SlccSplash.jsx";
import SlccGoals from "./components/SlccGoals.jsx";
export default function Slcc() {
var currentPageComponent;
const pages = [<SlccSplash />, <SlccGoals />, <SlccCourseView />];
const [currentPage, setCurrentPage] = useState(0);
if (currentPage === 0) {
currentPageComponent = pages[0];
} else if (currentPage === 1) {
currentPageComponent = pages[1];
} else if (currentPage === 2) {
currentPageComponent = pages[2];
}
return (
<>
<div className="center-column">
<SlccNav currentPage={currentPage} setCurrentPage={setCurrentPage} />
</div>
{currentPageComponent}
</>
);
}
+88
View File
@@ -0,0 +1,88 @@
import React, { useState } from "react";
import SlccCoursePage from "./components/SlccCoursePage.jsx";
export default function SlccCourseView() {
const courseData = [
{
name: "POLS 1100",
header: "POLS 1100 - Intro. to US Government and Politics",
sections: [
{
header: "Overview",
text: "I thoroughly enjoyed the assignments in POLS 1100, as I have always struggled to relay my thoughts effectively to others, and writing my political thoughts in this course seems to be a natural outlet for my internal dialogue. This course, paired with ENGL 1010, has inspired me to pursue writing -not as a career, but for my own enjoyment as I have truly come to enjoy the writing I have done over the Summer 2021 semester when I took these courses.",
pathToPdf: null,
},
{
header: "Public Policy Project",
text: "On this signature assignment, I wrote a research paper broken into four parts: defining the problems caused by unsafe storage of firearms, criteria to determine the efficacy of a policy, three possible policy solutions, and in-depth analysis on my chosen public policy solution, creating a tax credit for safe storage options. This project challenged me to think critically about the existing positions that I have regarding safe firearm storage, and to communicate those thoughts with data and clear speech. Part of why I chose this topic was because of my experience as the Political Director for March for our Lives Arizona, a youth-led gun violence prevention organization in which I advocated for the principles and policy agenda I write about. I enjoyed this assignment fully, and it acted as part of the push towards my own personal enjoyment of writing when paired with the Rhetorical Analysis Essay for ENGL 1010.",
pathToPdf: "/slcc/Public Policy Project.pdf",
},
{
header: "The First Amendment",
text: "In this essay, I examined how and why our First Amendment is the most critical foundation to the United States. These three essays allowed me to confront some big issues in our formal and informal political systems, that are unfortunately rarely mentioned in our political discourse. The thinking that I've had on these issues has largely remained the same but pushed towards writing to more clearly convey my thoughts and opinions. These essays in particular played an enormous role in kickstarting my long-dead love of writing and changed my perspective to approach life looking to learn.",
pathToPdf: "/slcc/Unit 1 Analytical Essay.pdf",
},
{
header: "The Danger of Hyper-Partisanship",
text: "Here, I investigated the Founder's Vision for the United States Congress -and how today we fail to meet that standard. These three essays allowed me to confront some big issues in our formal and informal political systems, that are unfortunately rarely mentioned in our political discourse. The thinking that I've had on these issues has largely remained the same but pushed towards writing to more clearly convey my thoughts and opinions. These essays in particular played an enormous role in kickstarting my long-dead love of writing and changed my perspective to approach life looking to learn.",
pathToPdf: "/slcc/Unit 2 Analytical Essay.pdf",
},
{
header: "Big Tech's Bubble Effect on Politics",
text: "In this paper, I scrutinized the role Big Tech has in our politics, particularly how the content we consume is designed to reinforce our existing beliefs. These three essays allowed me to confront some big issues in our formal and informal political systems, that are unfortunately rarely mentioned in our political discourse. The thinking that I've had on these issues has largely remained the same but pushed towards writing to more clearly convey my thoughts and opinions. These essays in particular played an enormous role in kickstarting my long-dead love of writing and changed my perspective to approach life looking to learn.",
pathToPdf: "/slcc/Unit 3 Analytical Essay.pdf",
},
],
},
{
name: "ENGL 1010",
header: "ENGL 1010 - Introduction to Writing",
sections: [
{
header: "Rhetorical Analysis",
text: "This signature assignment had a focus on examining the rhetorical devices authors use, and how they use those devices, to determine the effectiveness of their voice and argument.",
pathToPdf: "/slcc/Rhetorical Analysis.pdf",
},
{
header: "Portrait of My Writing Life",
text: "This assignment acts as a reflection of my writing over the Summer 2021 semester, where I've begun, and where I am now. I wrote about the two considerations that were talked about every day in class: audience and purpose, and practice.",
pathToPdf: "/slcc/Portrait of My Writing Life.pdf",
},
{
header: "Sensory Paper",
text: "This introductory essay had the task of writing a description of 5 senses in as much detail as possible. This essay in particular was a challenge for me, as I am unfamiliar and unpracticed with writing with sensory details.",
pathToPdf: "/slcc/Sensory Paper.pdf",
},
],
},
];
const [selectedCourse, setSelectedCourse] = useState(0);
const handleCourseSelect = (index) => {
setSelectedCourse(index);
};
return (
<>
<div id="course-view-container">
<div id="course-view-selector">
<div className="course-nav">
{courseData.map((course, index) => (
<div
key={index}
className={`course-nav-selector ${selectedCourse === index ? "course-nav-selector-active" : ""}`}
onClick={() => handleCourseSelect(index)}
>
{course.name}
</div>
))}
</div>
</div>
<div id="course-view">
{<SlccCoursePage course={courseData[selectedCourse]} />}
</div>
</div>
</>
);
}
+10 -10
View File
@@ -18,31 +18,31 @@ export default function PdfViewer({ file }) {
}
function next() {
if (pageNumber < numPages - 1) {
if (pageNumber < numPages) {
setPageNumber(pageNumber + 1);
}
}
console.log("numPages: " + numPages);
console.log("pageNumber: " + pageNumber);
return (
<>
<div className="pdf-container">
<button className="pdf-button shadow" onClick={() => prev()}>
<span className="material-icons">arrow_back</span>
</button>
<Document
file={file}
onLoadSuccess={({ numPages: numPagesInPdf }) => {
setNumPages(numPagesInPdf);
}}
className="pdf-document"
>
<Page pageNumber={pageNumber} />
</Document>
<button className="pdf-button shadow" onClick={() => next()}>
<span className="material-icons">arrow_forward</span>
</button>
<div className="pdf-buttons shadow">
<button className="pdf-button" onClick={() => prev()}>
<span className="material-icons">arrow_back</span>
</button>
<button className="pdf-button" onClick={() => next()}>
<span className="material-icons">arrow_forward</span>
</button>
</div>
</div>
</>
);
+32
View File
@@ -0,0 +1,32 @@
import React, { useRef, useEffect } from "react";
import { Swiper, SwiperSlide } from "swiper/react";
import { Navigation } from "swiper/modules";
import SlccCourseSection from "./SlccCourseSection";
// Import Swiper styles
import "swiper/css";
import "swiper/css/navigation";
export default function SlccCoursePage({ course }) {
return (
<>
<h2 className="course-header">{course.header}</h2>
<Swiper
navigation={true}
modules={[Navigation]}
className="course-page-slider"
>
{course.sections.map((section, index) => (
<SwiperSlide>
<SlccCourseSection
key={index}
header={section.header}
text={section.text}
pathToPdf={section.pathToPdf}
/>
</SwiperSlide>
))}
</Swiper>
</>
);
}
+16
View File
@@ -0,0 +1,16 @@
import React from "react";
import PdfViewer from "./PdfViewer";
export default function SlccCourseSection({ header, text, pathToPdf }) {
return (
<>
<div className="course-section">
<div className="course-section-text">
<h3>{header}</h3>
<p>{text}</p>
</div>
{pathToPdf !== null && <PdfViewer file={pathToPdf} />}{" "}
</div>
</>
);
}
+5
View File
@@ -0,0 +1,5 @@
import React from "react";
export default function SlccGoals() {
return <p>Hello cruel world</p>;
}
+56
View File
@@ -0,0 +1,56 @@
import React, { useState } from "react";
export default function SlccNav({ currentPage, setCurrentPage }) {
const [isActive, setIsActive] = useState(currentPage);
function navigateWelcome() {
if (isActive === 0 && currentPage === 0) return;
setIsActive(0);
setCurrentPage(0);
}
function navigateGoals() {
if (isActive === 1 && currentPage === 1) return;
setIsActive(1);
setCurrentPage(1);
}
function navigateCourseView() {
if (isActive === 2 && currentPage === 2) return;
setIsActive(2);
setCurrentPage(2);
}
return (
<>
<div className="nav">
<ul className="nav-list">
<li
className={`center-column nav-item ${isActive === 0 ? "nav-item-active" : ""}`}
onClick={() => navigateWelcome()}
>
Welcome
</li>
<li
className={`center-column nav-item ${isActive === 1 ? "nav-item-active" : ""}`}
onClick={() => navigateGoals()}
>
Goals & Outcomes
</li>
<li
className={`center-column nav-item ${isActive === 2 ? "nav-item-active" : ""}`}
onClick={() => navigateCourseView()}
>
Coursework
</li>
<li
className="center-column nav-sublist nav-item"
onClick={() => viewOutside()}
>
Outside Learning
</li>
</ul>
</div>
</>
);
}
+38
View File
@@ -0,0 +1,38 @@
import React from "react";
export default function SlccSplash() {
return (
<>
<div id="slcc-splash">
<div className="header">
<div className="header-image">
<img id="profile" src="/images/profile.jpg" />
</div>
<div className="header-text">
<div className="center-column">
<h1>Hi, I'm Josh Ashton.</h1>
<h3>
A Computer Science student at Salt Lake Community College.
</h3>
</div>
</div>
</div>
<div className="center-row">
<div className="text-section center-column">
<h3>About Me</h3>
<p className="center-column">
A mentor once told me that a Political Science degree is useless
if I want to pursue public policy work. They were absolutely
right. I'm pursuing a degree in Computer Science. The government
needs to be more proactive in regulating new developing
technologies, and I'm hopeful that one day I'll be able to bridge
that gap. Aside from computers, coding, and politics, some of my
other interests are Dungeons & Dragons, video games, backpacking,
and Doctor Who.
</p>
</div>
</div>
</div>
</>
);
}
-1
View File
@@ -4,7 +4,6 @@ import { Mousewheel, Pagination } from "swiper/modules";
import "swiper/css";
import "swiper/css/pagination";
import "../main.css";
export default function VerticalSlider({ slides }) {
return (
+200 -32
View File
@@ -55,6 +55,14 @@ h4 {
line-height: 1.5;
}
h2 {
font-size: clamp(2rem, 3vw, 4rem);
font-weight: 600;
letter-spacing: 2px;
width: max-content;
color: #ebd2ff;
}
h3 {
font-size: clamp(1.5rem, 2vw, 3rem);
}
@@ -90,6 +98,30 @@ p {
0 -6px 16px -6px hsla(0, 0%, 0%, 0.03);
}
.center-column {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.center-row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
text-align: center;
}
.text-section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 80%;
}
/**SWIPER PAGES**/
.swiper {
width: 100vw;
@@ -111,14 +143,16 @@ p {
}
/**ACTIVE PAGE ANIMATIONS**/
#header,
.header,
#skill-cards,
#project-section,
#project-cards-section,
#contact-section {
opacity: 0;
}
.swiper-slide-active #header,
.swiper-slide-active .header,
#slcc-splash .header,
.swiper-slide-active #skill-cards,
.swiper-slide-active #project-section,
.swiper-slide-active #project-cards-section,
@@ -126,7 +160,8 @@ p {
opacity: 0;
}
.swiper-slide-active #header,
.swiper-slide-active .header,
#slcc-splash .header,
.swiper-slide-active #skill-cards,
.swiper-slide-active #project-section,
.swiper-slide-active #project-cards-section,
@@ -135,7 +170,8 @@ p {
animation: moveDown 0.8s ease-in forwards;
}
.swiper-slide-active #header,
.swiper-slide-active .header,
#slcc-splash .header,
.swiper-slide-active #project-section,
.down-indicator {
animation-delay: 0.2s;
@@ -168,38 +204,38 @@ p {
height: 100vh;
grid-template-columns: 100%;
grid-template-rows: 40% 40% 10%;
column-gap: 10vh;
column-gap: 5%;
}
#header {
.header {
width: 100%;
overflow: hidden;
display: grid;
grid-template-columns: 55% 45%;
grid-template-columns: 30% 60%;
grid-template-rows: 100%;
}
#header-text {
.header-text {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-left: 120px;
text-align: center;
}
#header-image {
.header-image {
height: 40vh;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: var(--width);
margin: 0 auto;
}
#header-image img {
.header-image img {
width: calc(var(--width) / 2);
filter: drop-shadow(5px 6px 5px rgba(36, 36, 42, 0.5));
user-select: none;
@@ -449,29 +485,152 @@ p {
#resume-section {
margin: 2vw;
display: flex;
}
/**PDF Viewer**/
.pdf-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
max-width: 100%;
height: 70vh;
margin: 0 auto;
width: max-content;
display: grid;
grid-template-columns: 100%;
grid-template-rows: 90% 10%;
}
.pdf-document {
overflow: hidden;
height: 100%;
width: 50%;
width: max-content;
object-fit: scale-down;
}
.pdf-buttons {
width: 100%;
display: grid;
grid-template-rows: 100%;
grid-template-columns: 50% 50%;
}
.pdf-button {
width: 100%;
background-color: #ebd2ff;
color: #4a245e;
outline: none;
border: none;
padding: 1vw;
margin: 1vw;
transition-duration: 500ms;
}
.pdf-button span {
height: 100%;
width: 100%;
color: #4a245e;
font-size: clamp(1rem, 2rem, 3rem);
transition-duration: 500ms;
}
.pdf-button:hover,
.pdf-button:hover span {
background-color: #4a245e;
color: #ebd2ff;
}
/**SLCC Navigation Bar**/
.nav {
width: 90%;
margin-top: 3vw;
}
.nav-list {
display: grid;
grid-template-rows: 100%;
grid-template-columns: 25% 25% 25% 25%;
}
.nav-item {
border-left: 2px solid #4a245e;
border-right: 2px solid #4a245e;
font-size: clamp(1rem, 1.5vw, 1.25rem);
list-style: none;
padding: 2vw;
color: #4a245e;
background-color: #ebd2ff;
transition-duration: 500ms;
}
.nav-item:hover,
.nav-item-active {
background-color: #4a245e;
color: #ebd2ff;
}
/**SLCC Course View**/
#course-view-container {
height: 70vh;
width: 90%;
display: grid;
grid-template-columns: 10% 80%;
grid-template-rows: 100%;
margin: 50px auto;
}
#course-view-selector {
display: grid;
grid-template-rows: repeat(7, 10%);
}
.course-nav-selector {
border: 2px solid #4a245e;
background-color: #ebd2ff;
color: #4a245e;
height: 100%;
transition-duration: 500ms;
}
.course-nav-selector:hover,
.course-nav-selector-active {
color: #ebd2ff;
background-color: #4a245e;
}
.course-header {
margin-left: 2vw;
}
.course-section {
margin: 0 auto;
width: 90%;
display: grid;
grid-template-columns: 47.5% 47.5%;
column-gap: 5%;
}
.course-section-text {
width: 100%;
}
.course-page-slider {
width: 100%;
height: max-content;
}
.swiper-button-prev,
.swiper-rtl .swiper-button-next {
color: #ebd2ff !important;
}
.swiper-button-next,
.swiper-rtl .swiper-button-prev {
color: #ebd2ff !important;
}
.swiper-button-prev.swiper-button-disabled {
color: #ebd2ff !important;
}
.swiper-button-next.swiper-button-disabled {
color: #ebd2ff !important;
}
/**Animations**/
@@ -546,18 +705,34 @@ p {
}
@media (max-width: 790px) {
#primary {
#primary,
#slcc-splash {
width: 100%;
display: grid;
grid-template-rows: 40% 40% 10%;
grid-template-rows: 50% 50%;
grid-template-columns: 100%;
}
#header {
grid-template-columns: 60% 40%;
.header {
width: 100%;
display: grid;
grid-template-columns: 100%;
grid-template-rows: 50% 50%;
}
#header-text {
margin: 0;
.header .header-image {
max-height: 20vh;
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.header .header-text {
width: 100%;
}
h1 {
@@ -580,13 +755,6 @@ p {
font-size: clamp(0.5rem, 0.8rem, 1rem);
}
#header-image {
display: flex;
flex-direction: row;
align-items: center;
justify-content: left;
}
.skill-card-header {
border-radius: 18px 18px 18px 18px;
}
+2
View File
@@ -4,6 +4,7 @@ import { BrowserRouter as Router, Routes, Route } from "react-router";
import App from "./App.jsx";
import Resume from "./Resume.jsx";
import PageNotFound from "./PageNotFound.jsx";
import Slcc from "./Slcc.jsx";
const container = document.getElementById("app");
const root = createRoot(container); // createRoot(container!) if you use TypeScript
@@ -12,6 +13,7 @@ root.render(
<Routes>
<Route exact path="/" element={<App />} />
<Route exact path="/resume" element={<Resume />} />
<Route exact path="/slcc" element={<Slcc />} />
<Route path="*" element={<PageNotFound />} />
</Routes>
</Router>,