implemented vertical slider from Ecem Gokdogan

This commit is contained in:
Joshua Ashton
2024-12-21 16:05:49 -07:00
parent 874e88388a
commit 14c348af18
34 changed files with 1043 additions and 18502 deletions
BIN
View File
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 739 KiB

-17564
View File
File diff suppressed because it is too large Load Diff
-32
View File
@@ -1,32 +0,0 @@
{
"name": "react.dev",
"version": "0.0.0",
"main": "/src/index.js",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"dependencies": {
"bootstrap": "^5.3.3",
"react": "19.0.0-rc-3edc000d-20240926",
"react-bootstrap": "^2.10.7",
"react-dom": "19.0.0-rc-3edc000d-20240926",
"react-icons": "^5.4.0",
"react-router": "^7.0.2",
"react-scripts": "^5.0.0"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

-11
View File
@@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Josh Ashton</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
-13
View File
@@ -1,13 +0,0 @@
import Navigation from './components/Navigation';
import Footer from './components/Footer';
import Splash from './components/Splash';
export default function About() {
return (
<>
<Navigation />
<Splash primaryText="Under Construction" />
<Footer />
</>
);
}
-13
View File
@@ -1,13 +0,0 @@
import Navigation from './components/Navigation';
import Footer from './components/Footer';
import Splash from './components/Splash';
export default function Blog() {
return (
<>
<Navigation />
<Splash primaryText="Under Construction" />
<Footer />
</>
);
}
-13
View File
@@ -1,13 +0,0 @@
import Navigation from './components/Navigation';
import Footer from './components/Footer';
import Splash from './components/Splash';
export default function Contact() {
return (
<>
<Navigation />
<Splash primaryText="Under Construction" />
<Footer />
</>
);
}
-34
View File
@@ -1,34 +0,0 @@
import Navigation from './components/Navigation';
import Splash from './components/Splash';
import ProjectCard from './components/ProjectCard';
import Footer from './components/Footer';
import FeaturedContent from './components/FeaturedContent';
export default function Home( {projects} ) {
const projectCards = projects.map((project) => {
// Create a new ProjectCard object for each project in the 'projects' array
return (
<ProjectCard
key={project.id} // Add a unique key for each ProjectCard
frontend={project.frontend}
backend={project.backend}
id={project.id}
name={project.name}
description={project.description}
url={project.url}
img={project.img}
/>
);
});
return (
<>
<Navigation />
<Splash primaryText="Hi, I'm Josh Ashton!" secondaryText="Junior full stack developer."/>
<div className="row custom-container cards">
<h3 className="light-text">Featured Projects</h3>
<FeaturedContent content={projectCards} />
</div>
<Footer />
</>
);
}
-13
View File
@@ -1,13 +0,0 @@
import Navigation from './components/Navigation';
import Splash from './components/Splash';
import Footer from './components/Footer';
export default function Home( {projects} ) {
return (
<>
<Navigation />
<Splash primaryText="404" secondaryText="This is not the page you're looking for."/>
<Footer />
</>
);
}
-35
View File
@@ -1,35 +0,0 @@
import Navigation from './components/Navigation';
import Footer from './components/Footer';
import ProjectCard from './components/ProjectCard';
/*
* The Projects page.
*
* @param String[] projects "An array of attributes, frontend, backend, id, name, description, url, img, and imgCaption"
*/
export default function Projects( {projects} ) {
const projectCards = projects.map((project) => {
// Create a new ProjectCard object for each project in the 'projects' array
return (
<ProjectCard
key={project.id} // Add a unique key for each ProjectCard
frontend={project.frontend}
backend={project.backend}
id={project.id}
name={project.name}
description={project.description}
url={project.url}
img={project.img}
/>
);
});
return (
<>
<Navigation />
<div className="project-grid">
{projectCards}
</div>
<Footer />
</>
);
}
-13
View File
@@ -1,13 +0,0 @@
import Navigation from './components/Navigation';
import Footer from './components/Footer';
import Splash from './components/Splash';
export default function Resume() {
return (
<>
<Navigation />
<Splash primaryText="Under Construction" />
<Footer />
</>
);
}
-21
View File
@@ -1,21 +0,0 @@
/*
* Expects an array of Cards to display in a responsive grid.
*
* @param ProjectCard[] content "An array of ProjectCards or other Cards."
* TODO: Need to implement dynamic grid layout.
* TODO: Need to implement dynamic number of content, based on content size.
*/
export default function FeaturedContent( {content} ) {
return (
<>
<div className="row flex-row">
<div className="col">
{content[0]}
</div>
<div className="col">
{content[1]}
</div>
</div>
</>
);
}
-43
View File
@@ -1,43 +0,0 @@
import { FaLinkedinIn } from 'react-icons/fa';
import { FaInstagram } from 'react-icons/fa';
import { FaTwitterinIn } from 'react-icons/fa';
import { FaThreadsIn } from 'react-icons/fa';
/**
*
*
* TODO: Need to add classes and styling.
* TODO: Need to add links.
*/
export default function Footer() {
return (
<footer className="footer">
<div className="footer-container">
<div className="row">
<div className="col-md-6">
<p>&copy; {new Date().getFullYear()} Josh Ashton</p>
</div>
<div className="col-md-6">
<ul className="social-links">
<li><a href="/">Home</a></li>
<li><a href="/projects">Projects</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
<li><a href="/resume">Resume</a></li>
<li><a href="/blog">Blog</a></li>
<li>
<a href="https://www.linkedin.com/in/joshuatashton/" target="_blank" rel="noopener noreferrer">
<FaLinkedinIn />
</a>
</li>
<li>
<a href="https://www.instagram.com/joshuatashton/" target="_blank" rel="noopener noreferrer">
<FaInstagram />
</a>
</li>
</ul>
</div>
</div>
</div>
</footer>
);
}
-47
View File
@@ -1,47 +0,0 @@
import React, { useState } from 'react';
import Nav from 'react-bootstrap/Nav';
import Button from 'react-bootstrap/Button';
/*
* Create a nav bar component.
*
* Use Bootstrap for styling.
*
* TODO: Add links as a prop.
*/
export default function Navigation() {
const [isNavbarCollapsed, setIsNavbarCollapsed] = useState(false);
const toggleNavbar = () => {
setIsNavbarCollapsed(!isNavbarCollapsed);
};
return (
<>
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<a className="navbar-brand" href="/">Josh Ashton</a>
<button
className="navbar-toggler"
type="button"
onClick={toggleNavbar}
aria-controls="navbarNav"
aria-expanded={isNavbarCollapsed}
aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon"></span>
</button>
<div
className={`collapse navbar-collapse ${isNavbarCollapsed ? 'show' : ''}`}
id="navbarNav"
>
<ul className="navbar-nav nav-fill w-100">
<a className="nav-link" href="/projects"><li className="nav-item">Projects</li></a>
<a className="nav-link" href="/about"><li className="nav-item">About</li></a>
<a className="nav-link" href="/contact"><li className="nav-item">Contact</li></a>
<a className="nav-link" href="/resume"><li className="nav-item">Resume</li></a>
<a className="nav-link" href="/blog"><li className="nav-item">Blog</li></a>
</ul>
</div>
</nav>
</>
);
}
-28
View File
@@ -1,28 +0,0 @@
/*
* Create a ProjectCard.
*
* @param String frontend "The frontend framework/library used for the project. Used for sorting/filtering."
* @param String backend "The backend framework/library used for the project. Used for sorting/filtering."
* @param String id "Lowercase string representation of the project name."
* @param String name "The name of the project."
* @param String description "A description of the project."
* @param String url "The URL for the repository of the project."
* @param String img "Path to an image."
* @param String imgCaption "Accessibile caption for the image."
*
* TODO: Add project keys.
*/
export default function ProjectCard( {frontend, backend, id, name, description, url, img, imgCaption} ) {
return (
<>
<div className={`card ${frontend} ${backend}`}>
<img className="card-img-top" src={img} alt={imgCaption}/>
<div className="card-body">
<h2 className="card-title dark-text">{name}</h2>
<p className="dark-text">{description}</p>
<a className = "btn btn-primary" target="_blank" href={url}>View Code on GitHub</a>
</div>
</div>
</>
);
}
-41
View File
@@ -1,41 +0,0 @@
import React, { useState } from 'react';
import Button from 'react-bootstrap/Button';
/*
* Create a clickable frame which expands to expose additional information on click.
*
* @param String id "Lowercase string representation of the project name."
* @param String name "The name of the project."
* @param String description "A description of the project."
* @param String url "The URL for the repository of the project."
*
* TODO: Need to implement onClick
*/
export default function ProjectDisplay( {frontend, backend, id, name, description, url} ) {
const [isDisplayCollapsed, setIsDisplayCollapsed] = useState(false);
const toggleDisplay = () => {
setIsDisplayCollapsed(!isDisplayCollapsed);
}
return (
<>
<div
className={`panel ${frontend} ${backend} ${isDisplayCollapsed ? 'open open-active' : ''}`}
id={id}
onClick={toggleDisplay}
aria-expanded={!isDisplayCollapsed} // Invert the condition for aria-expanded
>
<h3 className="label">{name}</h3>
{isDisplayCollapsed && ( // Conditionally render additional content
<>
<h4 className="label-text">{description}</h4>
<button type="button" className="btn btn-primary">
<a target="_blank" href={url}>
View Code on GitHub
</a>
</button>
</>
)}
</div>
</>
);
}
-18
View File
@@ -1,18 +0,0 @@
/*
* Create a splash screen.
*
* @param String primaryText "The headline (h1) text."
* @param String secondaryText "The byline (h2) text."
*
* TODO: Need to add classes and styling.
*/
export default function Splash( { primaryText, secondaryText }) {
return (
<>
<div className="custom-container" id="splash">
<h1 className="light-text">{primaryText}</h1>
<h2 className="light-text">{secondaryText}</h2>
</div>
</>
);
}
-79
View File
@@ -1,79 +0,0 @@
import React, { StrictMode } from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter, Routes, Route } from "react-router";
import 'bootstrap/dist/css/bootstrap.min.css';
import "./styles.css";
import Home from "./Home";
import Projects from "./Projects";
import Contact from "./Contact";
import About from "./About";
import Blog from "./Blog";
import Resume from "./Resume";
import PageNotFound from "./PageNotFound";
const root = document.getElementById("root");
/*
* TEMPLATE:
{
frontend: "",
backend: "",
id: "",
name: "",
description: "",
url: "",
img: "",
imgCaption: ""
},
*/
const projects = [
{
frontend: "java",
backend: "",
id: "graph",
name: "Graph",
description: "Interactive representation of a graph data structure.",
url: "https://github.com/quaxlyqueen/2420-graph-visualizer",
img: "/assets/img/graph.png",
imgCaption: "A screenshot of my graph data structure visualizer application."
},
{
frontend: "flutter",
backend: "go",
id: "one-ai",
name: "OneAI",
description: "An end-to-end deployment wrapper for Meta's Ollama.",
url: "https://github.com/quaxlyqueen/one-ai",
img: "/assets/img/one-ai.png",
imgCaption: "A screenshot of a conversation with a self-hosted artificial intelligence."
},
{
frontend: "java",
backend: "",
id: "dnd",
name: "D&D",
description: "A Dungeons and Dragons character builder app.",
url: "https://github.com/quaxlyqueen/dnd",
img: "/assets/img/dnd.png",
imgCaption: "A screenshot of my Dungeons and Dragons character builder application."
},
];
ReactDOM.createRoot(root).render(
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Home projects={projects} />} />
<Route exact path="/projects" element={<Projects projects={projects} />} />
<Route exact path="/contact" element={<Contact />} />
<Route exact path="/about" element={<About />} />
<Route exact path="/blog" element={<Blog />} />
<Route exact path="/resume" element={<Resume />} />
<Route
path="*"
element={<PageNotFound />}
/>
</Routes>
</BrowserRouter>
);
-467
View File
@@ -1,467 +0,0 @@
:root {
--main-dark-color: #4A245E;
--main-offwhite-color: white;
--main-midtone-color: #9BC995;
--main-accent-color: #673283;
--secondary-accent-color: #98B9AB;
}
/* GENERAL */
body {
margin:0;
height:100vh;
background:
linear-gradient(
135deg,
hsl(279deg 33% 47%) 0%,
hsl(279deg 40% 33%) 39%,
hsl(279deg 62% 19%) 61%,
hsl(279deg 94% 7%) 100%
);
background-size:300% 100%,cover;
animation:change 4s linear infinite;
animation-direction: alternate;
background-attachment: fixed;
}
@keyframes change {
from {
background-position: right,center;
}
}
/* TEXT */
* {
font-family: Lato, sans-serif;
color: white;
}
h1, h2 {
font-weight: bold;
margin: 0;
}
h1 {
margin: 5vw 0 5vw 0;
font-size: 8vw;
}
h2 {
font-size: 4vw;
}
h3 {
font-size: 3vw;
}
h4 {
font-size: 2vw;
}
p {
font-size: 1vw;
}
.dark-text {
color: var(--main-dark-color);
}
.light-text {
color: var(--main-offwhite-color);
}
.label {
font-size: 2vw;
}
.label-text {
font-size: 1.5vw;
max-width: 80%;
text-align: center;
--white-space: nowrap;
--overflow: hidden;
--text-overflow: ellipsis;
}
/* NAV-BAR */
.bg-light {
background-color: white !important;
padding: 0;
}
.navbar-brand {
padding-left: .5vw;
color: var(--main-dark-color);
font-size: 2vw;
font-weight: bold;
animation: 500ms;
}
.navbar-toggler {
border: none;
}
.navbar-toggler:focus {
box-shadow: none;
border:none;
}
.navbar-light .navbar-toggler-icon {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(74, 36, 94, 1)' stroke-width='3.5' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
}
.navbar-brand:hover {
color: var(--main-dark-color);
}
.nav-link {
padding: 1.5vw;
}
.nav-link, .nav-link > .nav-item {
color: var(--main-dark-color);
font-size: 1.3vw;
animation: 500ms;
}
.nav-link:hover, .nav-link:hover > .nav-item {
color: var(--main-offwhite-color);
}
.nav-link:hover {
background-color: var(--main-dark-color);
}
.bar {
display: block;
width: 100%;
border-top: 5px var(--main-dark-color) solid;
padding-bottom: 1vw;
}
.carousel {
float: left;
margin-top: 5vw;
width: 55% !important;
}
input {
padding: 1em;
margin: .5em;
font-size: 1.2em;
}
.search {
padding: 1vw;
background-color: var(--main-dark-color);
color: var(--main-offwhite-color);
}
label, label {
display: inline-block;
border: 2px solid var(--main-offwhite-color);
border-radius: 5px;
text-align: center;
padding: .5em;
margin: 0 2em 1em 2em;
transition: 500ms;
}
input[type="radio"] {
opacity: 0;
position: fixed;
width: 0;
}
input[type="radio"]:checked + label {
background-color: var(--main-offwhite-color);
color: var(--main-dark-color);
}
input[type="radio"]:hover + label {
background-color: var(--main-offwhite-color);
color: var(--main-dark-color);
}
.panels {
flex: 1;
--overflow: hidden;
}
.panel {
max-height: 6.5vw;
background: var(--main-dark-color);
box-shadow: inset 0 0 0 5px rgba(255, 255, 255, 0.05);
color: var(--main-offwhite-color);
transition:
font-size 500ms,
flex 500ms,
padding 500ms,
max-height 750ms;
font-size: 2.5em;
background-position: center;
display: flex;
flex-direction: column;
overflow: hidden;
}
.panel > * {
width: 100%;
transition: transform 800ms;
flex: 1 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
.panel > *:nth-child(2) {
transform: translateX(-1000%);
}
.panel.open-active > *:nth-child(2) {
transform: translateX(12.5%);
}
.panel > *:last-child {
transform: translateX(-1000%);
transition: 800ms;
}
.panel.open-active > *:last-child {
transform: translateX(50%);
transition: 800ms;
}
.panel h3 {
font-family: Lato, sans-serif;
color: var(--main-offwhite-color);
font-size: 1.3em;
padding-top: 1vw;
}
.panel.open-active {
max-height: 20vw;
}
.panel.open {
overflow: visible;
flex: 5;
font-size: 2em;
}
.panel .label-text, .panel .btn {
display: none;
}
.panel.open .label-text, .panel.open .btn {
display: block;
}
.carousel-wrapper {
flex: 0 0 40%;
padding-top: 5vw;
}
.carousel {
float: right;
margin-top: 5vw;
width: 55%;
}
/* MISC */
.custom-container {
align-content: center;
padding: 5vw;
width: 90% !important;
margin-left: auto;
margin-right: auto;
}
.card {
display: flex; /* Enable flexbox layout */
flex-direction: column; /* Stack items vertically */
align-items: center; /* Center items horizontally */
justify-content: center; /* Center items vertically */
margin: 1vw;
}
.card img {
max-width: 50vw;
max-height: 35vw;
object-fit: cover;
}
.card-body {
text-align: center;
}
.card-text {
font-size: 2vw;
}
.flex-row {
display: flex; /* Enable flexbox layout */
height: 100%; /* Set the height of the container */
}
.flex-row > .col {
display: flex;
flex-direction: column; /* Ensure content flows vertically */
height: 100%; /* Make each column fill the container's height */
}
/*Buttons*/
.btn {
padding: 0.5vw;
background-color: var(--main-dark-color);
border: 2px solid var(--main-offwhite-color);
transition: background-color 300ms;
width: 50%;
margin-bottom: 1vw;
}
.btn:hover {
background-color: var(--main-accent-color);
}
.btn > a {
text-decoration: none;
color: var(--main-offwhite-color);
}
.footer-container {
margin-left: auto;
margin-right: auto;
width: 80% !important;
}
.social-links {
list-style: none; /* Remove default list styling */
display: flex; /* Display list items as flex items */
justify-content: space-between; /* Space items evenly */
}
.social-links li {
margin: 0 10px; /* Add spacing between icons */
}
.social-links li a {
text-decoration: none;
color: var(--main-offwhite-color);
}
.project-grid {
display: flex;
flex-wrap: wrap;
justify-content: space-between; /* Distribute projects evenly */
margin: 2vw auto; /* Center grid horizontally */
}
/* Responsive Breakpoints */
@media (min-width: 992px) { /* Large Screens (3 columns) */
.project-grid {
width: 80%; /* Adjust width as needed */
}
.project-grid > * {
flex: 0 0 30%; /* Set a fixed width for each project */
}
.footer-container .social-links {
width: 80% !important;
margin: 0;
padding-left: 0; /* Remove any left padding on mobile */
padding-right: 0; /* Remove any right padding on mobile */
}
}
@media (min-width: 768px) and (max-width: 991px) { /* Medium Screens (2 columns) */
.project-grid {
width: 80%; /* Adjust width as needed */
}
.project-grid > * {
flex: 0 0 40%; /* Set a fixed width for each project */
}
.footer-container .social-links {
width: 80% !important;
margin: 0;
padding-left: 0; /* Remove any left padding on mobile */
padding-right: 0; /* Remove any right padding on mobile */
}
}
@media (max-width: 767px) { /* Small Screens (1 column) */
.project-grid {
width: 80%;
}
.project-grid > * {
flex: 1 1 80%; /* Set full width for each project */
}
.footer-container .social-links {
width: 80% !important;
margin: 0;
padding-left: 0; /* Remove any left padding on mobile */
padding-right: 0; /* Remove any right padding on mobile */
}
.social-links li a {
font-size: 2vw;
}
h1 {
font-size: 8vw;
}
h2 {
font-size: 6vw;
}
h3, .navbar-brand {
font-size: 5vw;
}
h4 {
font-size: 3vw;
}
p, .nav-link, .nav-link > .nav-item {
font-size: 2vw;
}
}
@media (max-width: 991px) {
h1 {
font-size: 12vw;
}
h2 {
font-size: 8vw;
}
h3, .navbar-brand {
font-size: 6vw;
}
h4 {
font-size: 4vw;
}
p, .nav-link, .nav-link > .nav-item {
font-size: 3vw;
}
.nav-link > .nav-item {
font-weight: bold;
}
}