mirror of
https://github.com/noahpaige/portfolio-website.git
synced 2026-06-08 16:18:01 -06:00
paste in code from my other repo
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2021: true
|
||||||
|
},
|
||||||
|
extends: ['standard', 'plugin:react/recommended'],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
env: {
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
files: ['.eslintrc.{js,cjs}'],
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: 'script'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
plugins: ['react'],
|
||||||
|
rules: {
|
||||||
|
'react/prop-types': 0,
|
||||||
|
'react/jsx-indent': ['warn', 2]
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
+2
-3
@@ -2,12 +2,11 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Noah Paige</title>
|
<title>Noah Paige Portfolio</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Generated
+4013
-868
File diff suppressed because it is too large
Load Diff
+27
-13
@@ -2,26 +2,40 @@
|
|||||||
"name": "portfolio-site",
|
"name": "portfolio-site",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"homepage": "https://noahpaige.github.io/portfolio-website",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview"
|
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"predeploy": "npm run build",
|
||||||
|
"deploy": "gh-pages -d dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"daisyui": "^2.41.0",
|
"@headlessui/react": "^1.7.11",
|
||||||
|
"@heroicons/react": "^2.0.16",
|
||||||
|
"@joemaddalone/path": "^1.3.2",
|
||||||
|
"framer-motion": "^10.16.2",
|
||||||
|
"intersection-observer": "^0.12.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.8",
|
"@vitejs/plugin-react": "^1.0.7",
|
||||||
"@types/react": "^18.0.24",
|
"babel-eslint": "^10.1.0",
|
||||||
"@types/react-dom": "^18.0.8",
|
"daisyui": "^2.51.0",
|
||||||
"@vitejs/plugin-react": "^2.2.0",
|
"eslint": "^8.46.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"eslint-config-standard": "^17.1.0",
|
||||||
"postcss": "^8.4.19",
|
"eslint-plugin-import": "^2.28.0",
|
||||||
"tailwindcss": "^3.2.4",
|
"eslint-plugin-n": "^16.0.1",
|
||||||
"typescript": "^4.6.4",
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
"vite": "^3.2.3"
|
"eslint-plugin-react": "^7.33.1",
|
||||||
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.3",
|
||||||
|
"gh-pages": "^5.0.0",
|
||||||
|
"postcss": "^8.4.7",
|
||||||
|
"react-router-dom": "^6.14.2",
|
||||||
|
"tailwindcss": "^3.2.7",
|
||||||
|
"vite": "^4.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: [require('tailwindcss'), require('autoprefixer')],
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: [require('tailwindcss')],
|
||||||
|
};
|
||||||
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1 @@
|
|||||||
|
https://stackblitz.com/edit/daisyui-react-vite-headlessui?file=package.json
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const openSpring = { type: 'spring', stiffness: 200, damping: 30 }
|
||||||
|
export const closeSpring = { type: 'spring', stiffness: 300, damping: 35 }
|
||||||
+4
-5
@@ -2,8 +2,7 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
html,
|
::-webkit-scrollbar {
|
||||||
body {
|
width: 0;
|
||||||
height: 100vh;
|
}
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
import './App.css'
|
||||||
|
import Home from './Pages/Home'
|
||||||
|
import About from './Pages/About'
|
||||||
|
import Skills from './Pages/Skills'
|
||||||
|
import Projects from './Pages/Projects'
|
||||||
|
import Links from './Pages/Links'
|
||||||
|
import Section from './Components/Section'
|
||||||
|
import React, { useRef, useState } from 'react'
|
||||||
|
import { BrowserRouter } from 'react-router-dom'
|
||||||
|
import Color from './Classes/Color'
|
||||||
|
import { EventBus } from './Classes/EventBus'
|
||||||
|
|
||||||
|
export const SiteContext = React.createContext({ isNavigating: false })
|
||||||
|
|
||||||
|
const sections = [
|
||||||
|
{
|
||||||
|
comp: <Home />,
|
||||||
|
path: '/',
|
||||||
|
topColor: new Color().set({ r: 100, g: 100, b: 200 }),
|
||||||
|
bottomColor: new Color().set({ r: 200, g: 100, b: 100 })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
comp: <About />,
|
||||||
|
path: '/about',
|
||||||
|
topColor: new Color().set({ r: 200, g: 100, b: 100 }),
|
||||||
|
bottomColor: new Color().set({ r: 100, g: 200, b: 100 })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
comp: <Skills />,
|
||||||
|
path: '/skills',
|
||||||
|
topColor: new Color().set({ r: 100, g: 200, b: 100 }),
|
||||||
|
bottomColor: new Color().set({ r: 100, g: 200, b: 200 })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
comp: <Projects />,
|
||||||
|
path: '/projects',
|
||||||
|
topColor: new Color().set({ r: 100, g: 200, b: 200 }),
|
||||||
|
bottomColor: new Color().set({ r: 200, g: 100, b: 200 })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
comp: <Links />,
|
||||||
|
path: '/links',
|
||||||
|
topColor: new Color().set({ r: 200, g: 100, b: 200 }),
|
||||||
|
bottomColor: new Color().set({ r: 100, g: 100, b: 200 })
|
||||||
|
}
|
||||||
|
]
|
||||||
|
function App () {
|
||||||
|
const [state, setState] = useState({ isNavigating: false })
|
||||||
|
const [allowScroll, setAllowScroll] = useState(true)
|
||||||
|
const scrollRef = useRef()
|
||||||
|
|
||||||
|
EventBus.$on('toggleBodyScroll', () => {
|
||||||
|
setAllowScroll((prev) => !prev)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BrowserRouter>
|
||||||
|
<div className="App text-slate-200">
|
||||||
|
<SiteContext.Provider value={{ state, setState }}>
|
||||||
|
<div className={`snap-y snap-mandatory h-screen overflow-x-hidden ${allowScroll ? 'overflow-y-scroll' : 'overflow-y-hidden'}`} ref={scrollRef}>
|
||||||
|
{sections.map((section, index) => (
|
||||||
|
<Section
|
||||||
|
key={index}
|
||||||
|
content={section.comp}
|
||||||
|
path={section.path}
|
||||||
|
topColor={section.topColor}
|
||||||
|
bottomColor={section.bottomColor} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</SiteContext.Provider>
|
||||||
|
</div>
|
||||||
|
</BrowserRouter>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
-99
@@ -1,99 +0,0 @@
|
|||||||
import { useState, useRef } from "react";
|
|
||||||
import "./App.css";
|
|
||||||
//comment
|
|
||||||
function App() {
|
|
||||||
const [count, setCount] = useState(0);
|
|
||||||
const scrollRef = useRef(null);
|
|
||||||
const messages = [
|
|
||||||
"It's over Anakin! I have the high ground.",
|
|
||||||
"YoU uNdErEsTiMaTe My PoWeR!!",
|
|
||||||
"It was said that you would, destroy the Sith, not join them.",
|
|
||||||
"You were my brother, Anakin.",
|
|
||||||
];
|
|
||||||
|
|
||||||
const curves = [
|
|
||||||
"M0,192L48,176C96,160,192,128,288,112C384,96,480,96,576,101.3C672,107,768,117,864,138.7C960,160,1056,192,1152,181.3C1248,171,1344,117,1392,90.7L1440,64L1440,0L1392,0C1344,0,1248,0,1152,0C1056,0,960,0,864,0C768,0,672,0,576,0C480,0,384,0,288,0C192,0,96,0,48,0L0,0Z",
|
|
||||||
"M0,192L48,202.7C96,213,192,235,288,213.3C384,192,480,128,576,122.7C672,117,768,171,864,181.3C960,192,1056,160,1152,170.7C1248,181,1344,235,1392,261.3L1440,288L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z",
|
|
||||||
"M0,256L48,261.3C96,267,192,277,288,245.3C384,213,480,139,576,101.3C672,64,768,64,864,85.3C960,107,1056,149,1152,160C1248,171,1344,149,1392,138.7L1440,128L1440,0L1392,0C1344,0,1248,0,1152,0C1056,0,960,0,864,0C768,0,672,0,576,0C480,0,384,0,288,0C192,0,96,0,48,0L0,0Z",
|
|
||||||
"M0,32L48,64C96,96,192,160,288,165.3C384,171,480,117,576,112C672,107,768,149,864,176C960,203,1056,213,1152,192C1248,171,1344,117,1392,90.7L1440,64L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z",
|
|
||||||
"M0,96L48,122.7C96,149,192,203,288,229.3C384,256,480,256,576,240C672,224,768,192,864,165.3C960,139,1056,117,1152,144C1248,171,1344,245,1392,282.7L1440,320L1440,0L1392,0C1344,0,1248,0,1152,0C1056,0,960,0,864,0C768,0,672,0,576,0C480,0,384,0,288,0C192,0,96,0,48,0L0,0Z",
|
|
||||||
"M0,160L48,170.7C96,181,192,203,288,186.7C384,171,480,117,576,112C672,107,768,149,864,186.7C960,224,1056,256,1152,250.7C1248,245,1344,203,1392,181.3L1440,160L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z",
|
|
||||||
"M0,64L48,101.3C96,139,192,213,288,218.7C384,224,480,160,576,128C672,96,768,96,864,133.3C960,171,1056,245,1152,250.7C1248,256,1344,192,1392,160L1440,128L1440,0L1392,0C1344,0,1248,0,1152,0C1056,0,960,0,864,0C768,0,672,0,576,0C480,0,384,0,288,0C192,0,96,0,48,0L0,0Z",
|
|
||||||
"M0,192L48,181.3C96,171,192,149,288,154.7C384,160,480,192,576,186.7C672,181,768,139,864,149.3C960,160,1056,224,1152,234.7C1248,245,1344,203,1392,181.3L1440,160L1440,320L1392,320C1344,320,1248,320,1152,320C1056,320,960,320,864,320C768,320,672,320,576,320C480,320,384,320,288,320C192,320,96,320,48,320L0,320Z",
|
|
||||||
"M0,192L48,202.7C96,213,192,235,288,213.3C384,192,480,128,576,122.7C672,117,768,171,864,181.3C960,192,1056,160,1152,170.7C1248,181,1344,235,1392,261.3L1440,288L1440,0L1392,0C1344,0,1248,0,1152,0C1056,0,960,0,864,0C768,0,672,0,576,0C480,0,384,0,288,0C192,0,96,0,48,0L0,0Z",
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={scrollRef} className="overflow-scroll">
|
|
||||||
<div className="snap-mandatory snap-y overflow-scroll h-screen bg-base-200">
|
|
||||||
{messages.map(function (message, i) {
|
|
||||||
return (
|
|
||||||
<div className="snap-center h-screen" key={message}>
|
|
||||||
<div className="hero h-screen relative ">
|
|
||||||
<div className="hero-content text-center">
|
|
||||||
<div className="max-w-md">
|
|
||||||
<h1 className="text-5xl font-bold">{message}</h1>
|
|
||||||
<p className="py-6">
|
|
||||||
Provident cupiditate voluptatem et in. Quaerat fugiat ut
|
|
||||||
assumenda excepturi exercitationem quasi. In deleniti
|
|
||||||
eaque aut repudiandae et a id nisi.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="relative h-screen w-screen -translate-y-full">
|
|
||||||
<div className="absolute top-0 bg-blue-500 h-12 w-screen" />
|
|
||||||
<svg
|
|
||||||
className="drop-shadow-xl absolute top-11 fill-blue-500 "
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 1440 320"
|
|
||||||
preserveAspectRatio="none"
|
|
||||||
height="100"
|
|
||||||
width="100%"
|
|
||||||
>
|
|
||||||
<path fillOpacity="1" d={curves[i * 2]}></path>
|
|
||||||
</svg>
|
|
||||||
<svg
|
|
||||||
className="drop-shadow-xl absolute bottom-11 fill-blue-500"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 1440 320"
|
|
||||||
preserveAspectRatio="none"
|
|
||||||
height="100"
|
|
||||||
width="100%"
|
|
||||||
>
|
|
||||||
<path fillOpacity="1" d={curves[i * 2 + 1]}></path>
|
|
||||||
</svg>
|
|
||||||
<div className="absolute bottom-0 bg-blue-500 h-12 w-screen" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
<footer className="footer p-10 bg-blue-500 text-neutral-content snap-center">
|
|
||||||
<div>
|
|
||||||
<span className="footer-title">Services</span>
|
|
||||||
<a className="link link-hover">Branding</a>
|
|
||||||
<a className="link link-hover">Design</a>
|
|
||||||
<a className="link link-hover">Marketing</a>
|
|
||||||
<a className="link link-hover">Advertisement</a>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="footer-title">Company</span>
|
|
||||||
<a className="link link-hover">About us</a>
|
|
||||||
<a className="link link-hover">Contact</a>
|
|
||||||
<a className="link link-hover">Jobs</a>
|
|
||||||
<a className="link link-hover">Press kit</a>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="footer-title">Legal</span>
|
|
||||||
<a className="link link-hover">Terms of use</a>
|
|
||||||
<a className="link link-hover">Privacy policy</a>
|
|
||||||
<a className="link link-hover">Cookie policy</a>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
class Color {
|
||||||
|
constructor () {
|
||||||
|
this.r = 0
|
||||||
|
this.g = 0
|
||||||
|
this.b = 0
|
||||||
|
this.a = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
set (o) {
|
||||||
|
if (typeof o.r === 'number') this.r = Math.min(Math.max(0, o.r), 255)
|
||||||
|
if (typeof o.g === 'number') this.g = Math.min(Math.max(0, o.g), 255)
|
||||||
|
if (typeof o.b === 'number') this.b = Math.min(Math.max(0, o.b), 255)
|
||||||
|
if (typeof o.a === 'number') this.a = Math.min(Math.max(0, o.a), 1)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
toObj () {
|
||||||
|
return JSON.parse(JSON.stringify(
|
||||||
|
{ r: this.r, g: this.g, b: this.b, a: this.a }
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
toString () {
|
||||||
|
return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`
|
||||||
|
}
|
||||||
|
|
||||||
|
static blend (a, b, i) {
|
||||||
|
const c = new Color()
|
||||||
|
const ao = a.toObj()
|
||||||
|
const bo = b.toObj()
|
||||||
|
c.set({ r: lerp(ao.r, bo.r, i), g: lerp(ao.g, bo.g, i), b: lerp(ao.b, bo.b, i), a: lerp(ao.a, bo.a, i) })
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lerp = (a, b, i) => a + (b - a) * i
|
||||||
|
|
||||||
|
export default Color
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
class _EventBus {
|
||||||
|
constructor () {
|
||||||
|
this.bus = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
$off (id) {
|
||||||
|
delete this.bus[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
$on (id, callback) {
|
||||||
|
this.bus[id] = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
$emit (id, ...params) {
|
||||||
|
if (this.bus[id]) { this.bus[id](...params) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EventBus = new _EventBus()
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import React, { memo } from 'react'
|
||||||
|
import { motion } from 'framer-motion'
|
||||||
|
|
||||||
|
const ChromaticText = ({ text, offset }) => {
|
||||||
|
return (
|
||||||
|
<div className='relative'>
|
||||||
|
<motion.span
|
||||||
|
animate={{ margin: `-${offset} ${offset} ${offset} -${offset}` }}
|
||||||
|
className={'absolute text-red-500 select-none'}>
|
||||||
|
{text}
|
||||||
|
</motion.span>
|
||||||
|
<motion.span
|
||||||
|
animate={{ margin: `${offset} ${offset} ${offset} ${offset}` }}
|
||||||
|
className={'absolute text-blue-500 select-none'}>
|
||||||
|
{text}
|
||||||
|
</motion.span>
|
||||||
|
<span className='text-slate-200 z-10 mix-blend-lighten'>{text}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ChromaticText)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import React, { memo } from 'react'
|
||||||
|
import ProjectImage from './ProjectImage'
|
||||||
|
import ProjectTitle from './ProjectTitle'
|
||||||
|
import Scroller from './Scroller'
|
||||||
|
|
||||||
|
const ProjectCard = ({ isOpen, title, imgSrc, imgPOI, content, tags }) => {
|
||||||
|
return (
|
||||||
|
<div className='backdrop-blur-xl bg-[rgba(20,30,40,0.7)] h-full'>
|
||||||
|
<Scroller height={isOpen ? '100%' : 'fit-content'} allowScroll={isOpen}>
|
||||||
|
<ProjectImage isOpen={isOpen} imgSrc={imgSrc} width={'800px'} height={'400px'} imgPOI={imgPOI} title={title}>
|
||||||
|
<ProjectTitle isOpen={isOpen} title={title} tags={tags} width={'800px'} height={'400px'} />
|
||||||
|
</ProjectImage>
|
||||||
|
|
||||||
|
<div className='p-4' >
|
||||||
|
{ content }
|
||||||
|
</div>
|
||||||
|
</Scroller>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ProjectCard)
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import React, { memo, useRef, useState } from 'react'
|
||||||
|
import { motion, useAnimate, useMotionValueEvent, useScroll } from 'framer-motion'
|
||||||
|
import { openSpring, closeSpring } from '../Animations'
|
||||||
|
import ProjectCard from './ProjectCard'
|
||||||
|
import { EventBus } from '../Classes/EventBus'
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
open: {
|
||||||
|
card: {
|
||||||
|
width: '80vw',
|
||||||
|
height: '90vh',
|
||||||
|
left: '10vw',
|
||||||
|
zIndex: '30'
|
||||||
|
},
|
||||||
|
overlay: {
|
||||||
|
part1: {
|
||||||
|
width: '100vw',
|
||||||
|
height: '100vh',
|
||||||
|
zIndex: '20'
|
||||||
|
},
|
||||||
|
|
||||||
|
part2: {
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
closed: {
|
||||||
|
card: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
left: '0'
|
||||||
|
},
|
||||||
|
overlay: {
|
||||||
|
part1: {
|
||||||
|
opacity: 0,
|
||||||
|
zIndex: '10'
|
||||||
|
},
|
||||||
|
part2: {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ProjectCardContainer = ({ title, imgSrc, imgPOI, content, tags, onCardToggle }) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
const [cardRef, animCard] = useAnimate()
|
||||||
|
const [overlayRef, animOverlay] = useAnimate()
|
||||||
|
const outerEl = useRef()
|
||||||
|
const { scrollY } = useScroll({ container: outerEl })
|
||||||
|
|
||||||
|
useMotionValueEvent(scrollY, 'change', (latest) => {
|
||||||
|
console.log('Page scroll: ', latest)
|
||||||
|
})
|
||||||
|
|
||||||
|
const onCardClick = () => {
|
||||||
|
if (!isOpen) {
|
||||||
|
setIsOpen(true)
|
||||||
|
EventBus.$emit('toggleBodyScroll')
|
||||||
|
openAnimation(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOverlayClick = () => {
|
||||||
|
if (isOpen) {
|
||||||
|
setIsOpen(false)
|
||||||
|
EventBus.$emit('toggleBodyScroll')
|
||||||
|
closeAnimation(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openAnimation () {
|
||||||
|
const outerRect = outerEl.current.getBoundingClientRect()
|
||||||
|
|
||||||
|
await animOverlay(overlayRef.current, { ...variants.open.overlay.part1, x: -outerRect.x, y: -outerRect.y }, { duration: 0 })
|
||||||
|
await Promise.all([
|
||||||
|
animOverlay(overlayRef.current, { ...variants.open.overlay.part2 }, openSpring),
|
||||||
|
animCard(cardRef.current, { ...variants.open.card, x: -outerRect.x, y: `calc(5vh - ${outerRect.y}px)` }, openSpring)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
async function closeAnimation () {
|
||||||
|
await Promise.all([
|
||||||
|
animCard(cardRef.current, { ...variants.closed.card, x: 0, y: 0 }, closeSpring),
|
||||||
|
animOverlay(overlayRef.current, { ...variants.closed.overlay.part1 }, closeSpring)
|
||||||
|
])
|
||||||
|
await animOverlay(overlayRef.current, { ...variants.closed.overlay.part2, x: 0, y: 0 }, { duration: 0 })
|
||||||
|
|
||||||
|
await animCard(cardRef.current, { zIndex: '10' })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={outerEl} className='relative'>
|
||||||
|
<motion.div
|
||||||
|
ref={overlayRef}
|
||||||
|
whileHover={{ cursor: isOpen && 'pointer' }}
|
||||||
|
onClick={onOverlayClick}
|
||||||
|
className='absolute backdrop-blur-sm bg-slate-900/50' />
|
||||||
|
<motion.div
|
||||||
|
ref={cardRef}
|
||||||
|
style={{ cursor: !isOpen && 'pointer' }}
|
||||||
|
whileHover={{ scale: isOpen ? 1 : 1.02 }}
|
||||||
|
className={'absolute bg-slate-700 h-full w-full flex flex-col shadow-xl bg-opacity-10 overflow-hidden rounded-xl'}
|
||||||
|
onClick={onCardClick}
|
||||||
|
>
|
||||||
|
<ProjectCard isOpen={isOpen} imgSrc={imgSrc} imgPOI={imgPOI} title={title} content={content} tags={tags} />
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ProjectCardContainer)
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import React, { useEffect, useRef, useState, memo } from 'react'
|
||||||
|
import { motion } from 'framer-motion'
|
||||||
|
|
||||||
|
const ProjectImage = ({ isOpen, imgSrc, width, height, imgPOI, title, children }) => {
|
||||||
|
const containerRef = useRef()
|
||||||
|
const imgRef = useRef()
|
||||||
|
const [offset, setOffset] = useState({ x: 0, y: 0 })
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const cRect = containerRef.current.getBoundingClientRect()
|
||||||
|
const iRect = imgRef.current.getBoundingClientRect()
|
||||||
|
|
||||||
|
console.log('here')
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<motion.div
|
||||||
|
ref={containerRef}
|
||||||
|
className='h-[400px] w-full overflow-hidden'
|
||||||
|
style={{ originX: 0, originY: 0 }}
|
||||||
|
>
|
||||||
|
<div className='relative'>
|
||||||
|
<motion.img
|
||||||
|
ref={imgRef}
|
||||||
|
src={imgSrc}
|
||||||
|
alt={title + ' Image'}
|
||||||
|
className='object-cover w-full h-[inherit]'
|
||||||
|
initial={false}
|
||||||
|
animate={ isOpen ? { x: 0, y: 0 } : { x: -imgPOI.x, y: imgPOI.y } }
|
||||||
|
/>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</motion.div>
|
||||||
|
</>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ProjectImage)
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import React, { memo, useEffect, useState } from 'react'
|
||||||
|
import { motion } from 'framer-motion'
|
||||||
|
import ChromaticText from './ChromaticText'
|
||||||
|
import { openSpring, closeSpring } from '../Animations'
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
open: {
|
||||||
|
fontSize: '8rem',
|
||||||
|
transition: { openSpring }
|
||||||
|
},
|
||||||
|
closed: {
|
||||||
|
fontSize: '3rem',
|
||||||
|
transition: { closeSpring }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProjectTitle = ({ isOpen, title, tags, width, height }) => {
|
||||||
|
const [isHovered, setIsHovered] = useState(false)
|
||||||
|
const [textOffset, setTextOffset] = useState('0.05rem')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isHovered && !isOpen) setTextOffset('0.05rem')
|
||||||
|
if (isHovered && !isOpen) setTextOffset('0.2rem')
|
||||||
|
if (!isHovered && isOpen) setTextOffset('0.2rem')
|
||||||
|
if (isHovered && isOpen) setTextOffset('0.3rem')
|
||||||
|
}, [isOpen, isHovered])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={'absolute top-0 w-full flex flex-col h-full'}
|
||||||
|
style={{ height: isOpen && height }}
|
||||||
|
onMouseOver={() => setIsHovered(true)}
|
||||||
|
onMouseOut={() => setIsHovered(false)}
|
||||||
|
onMouseDown={() => !isOpen && setIsHovered(true)}
|
||||||
|
onMouseUp={() => !isOpen && setIsHovered(false)}
|
||||||
|
>
|
||||||
|
<div className='ml-2 [&>*:nth-child(1)]:ml-0'>
|
||||||
|
{tags.map((tag, index) => (
|
||||||
|
<motion.span
|
||||||
|
className='badge bg-[rgba(20,30,40,0.3)] backdrop-blur-lg border-none mx-1 my-2 text-slate-100'
|
||||||
|
animate={isOpen ? { margin: '1rem 0.5rem 1rem 0.5rem', fontSize: '1.5rem', height: '2.25rem' } : { margin: '0.5rem 0.25rem 0.5rem 0.25rem', fontSize: '1rem', height: '1.5rem' }}
|
||||||
|
key={index}>
|
||||||
|
{tag}
|
||||||
|
</motion.span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<motion.div
|
||||||
|
animate={isOpen ? 'open' : 'closed'}
|
||||||
|
initial={'closed'}
|
||||||
|
variants={variants}
|
||||||
|
className='ml-2 font-bold'
|
||||||
|
>
|
||||||
|
<ChromaticText text={title} offset={textOffset}/>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ProjectTitle)
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import React, { useRef, memo } from 'react'
|
||||||
|
import { motion, useScroll, useSpring } from 'framer-motion'
|
||||||
|
|
||||||
|
const Scroller = ({ height, children, allowScroll, allowOverscroll = false }) => {
|
||||||
|
const scrollRef = useRef(null)
|
||||||
|
const barRef = useRef(null)
|
||||||
|
const { scrollYProgress } = useScroll({ container: scrollRef })
|
||||||
|
const scaleY = useSpring(scrollYProgress, {
|
||||||
|
stiffness: 60,
|
||||||
|
damping: 15,
|
||||||
|
restDelta: 0.1
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={scrollRef}
|
||||||
|
className={'w-full scroll-smooth'}
|
||||||
|
style={{
|
||||||
|
height,
|
||||||
|
overscrollBehavior: allowOverscroll ? 'auto' : 'contain',
|
||||||
|
overflow: allowScroll ? 'scroll' : 'hidden'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className='fixed h-full w-[12px] z-40 right-0' ref={barRef}>
|
||||||
|
<motion.div
|
||||||
|
className='bg-red-500 w-full h-full origin-top'
|
||||||
|
style={{
|
||||||
|
scaleY,
|
||||||
|
visibility: allowScroll ? 'visible' : 'hidden'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
<div >
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Scroller)
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import React, { useContext, useEffect, useState, useRef } from 'react'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import useIsInViewport from '../Hooks/useIsInViewport'
|
||||||
|
import { SiteContext } from '../App'
|
||||||
|
import useBuildCurves from '../Hooks/useBuildCurves'
|
||||||
|
import Color from '../Classes/Color'
|
||||||
|
|
||||||
|
//A test comment
|
||||||
|
|
||||||
|
function Section ({ content, path, topColor, bottomColor }) {
|
||||||
|
const { state, setState } = useContext(SiteContext)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const ruff = useRef()
|
||||||
|
const bgColor = new Color().set({ r: 20, g: 15, b: 30 })
|
||||||
|
const curveHeight = 2
|
||||||
|
const numCurves = 5
|
||||||
|
const [curves, setCurves] = useState({ tops: [], bottoms: [] })
|
||||||
|
const handleIsInView = () => {
|
||||||
|
if (window.location.pathname.toLowerCase() !== path) navigate(path)
|
||||||
|
}
|
||||||
|
const isInViewport = useIsInViewport(ruff, 0.9, handleIsInView)
|
||||||
|
|
||||||
|
useEffect(() => { setCurves(useBuildCurves(numCurves, curveHeight, topColor, bottomColor, bgColor)) }, [])
|
||||||
|
useEffect(() => {
|
||||||
|
if (window.location.pathname.toLowerCase() === path && !state.isNavigating) {
|
||||||
|
ruff.current.scrollIntoView({ behavior: 'smooth' }) // scroll to this section
|
||||||
|
setState({ isNavigating: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (isInViewport) setState({ isNavigating: false })
|
||||||
|
}
|
||||||
|
}, [window, ruff, state])
|
||||||
|
return (
|
||||||
|
<div ref={ruff} className="snap-center h-full w-full relative" style={{ backgroundColor: bgColor.toString() }}>
|
||||||
|
{curves.tops.map((curve, index) => (
|
||||||
|
<svg
|
||||||
|
className='w-full h-full absolute top-0 pointer-events-none overflow-hidden'
|
||||||
|
viewBox={`0 ${100 - ((numCurves - index) * curveHeight)} 100 100`}
|
||||||
|
preserveAspectRatio="none"
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
<path d={curve.path} stroke="transparent" fill={curve.color} />
|
||||||
|
</svg>
|
||||||
|
))}
|
||||||
|
{curves.bottoms.map((curve, index) => (
|
||||||
|
<svg
|
||||||
|
className='w-full h-full absolute bottom-0 pointer-events-none'
|
||||||
|
viewBox={`0 ${-100 + ((numCurves - index) * curveHeight)} 100 100`}
|
||||||
|
preserveAspectRatio="none"
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
<path d={curve.path} stroke="transparent" fill={curve.color} />
|
||||||
|
</svg>
|
||||||
|
))}
|
||||||
|
<div className='h-full w-full p-8 overflow-overlay' style={{ paddingTop: curveHeight * numCurves + 'vh', paddingBottom: curveHeight * numCurves + 'vh' }}>{content}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Section
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const projectsData = [
|
||||||
|
{
|
||||||
|
id: 'smarter-npcs',
|
||||||
|
title: 'Smarter NPCs',
|
||||||
|
tags: ['Unity', 'Game Dev', 'C#'],
|
||||||
|
imgSrc: 'https://cdn.pixabay.com/photo/2023/05/01/06/17/pelican-7962189_1280.jpg',
|
||||||
|
imgPOI: { x: 0, y: -100 },
|
||||||
|
desc: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Odio morbi quis commodo odio aenean sed. Id porta nibh venenatis cras sed felis eget velit aliquet. Varius sit amet mattis vulputate enim nulla. ',
|
||||||
|
content: <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Blandit cursus risus at ultrices mi tempus imperdiet nulla malesuada. Fusce ut placerat orci nulla pellentesque dignissim enim sit. Lorem ipsum dolor sit amet consectetur. Convallis aenean et tortor at risus viverra adipiscing at in. Libero id faucibus nisl tincidunt eget. Proin nibh nisl condimentum id venenatis. Nibh sit amet commodo nulla. Suscipit tellus mauris a diam maecenas sed enim. Malesuada pellentesque elit eget gravida cum sociis natoque penatibus. Pulvinar sapien et ligula ullamcorper malesuada proin libero. Nunc consequat interdum varius sit amet mattis vulputate enim nulla. Nunc aliquet bibendum enim facilisis gravida neque convallis a cras. Placerat duis ultricies lacus sed turpis. Malesuada bibendum arcu vitae elementum curabitur vitae nunc sed. Massa ultricies mi quis hendrerit dolor magna eget est. Eu sem integer vitae justo eget magna. Orci eu lobortis elementum nibh. Tempus imperdiet nulla malesuada pellentesque elit eget gravida. Diam sollicitudin tempor id eu nisl nunc mi ipsum.
|
||||||
|
|
||||||
|
Eu nisl nunc mi ipsum faucibus vitae. Pharetra massa massa ultricies mi quis hendrerit. Turpis in eu mi bibendum. Nibh nisl condimentum id venenatis a. Sapien pellentesque habitant morbi tristique. Pharetra diam sit amet nisl. Mi in nulla posuere sollicitudin aliquam ultrices. Maecenas volutpat blandit aliquam etiam. Suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Cras ornare arcu dui vivamus. Ut consequat semper viverra nam. Auctor neque vitae tempus quam pellentesque nec nam. Quis blandit turpis cursus in hac habitasse. Congue eu consequat ac felis donec. Urna condimentum mattis pellentesque id nibh tortor id aliquet.
|
||||||
|
|
||||||
|
Cras pulvinar mattis nunc sed blandit libero volutpat. Nunc sed augue lacus viverra vitae congue eu. Proin libero nunc consequat interdum varius sit. Risus pretium quam vulputate dignissim suspendisse in. In est ante in nibh mauris cursus. Lacinia quis vel eros donec ac odio tempor orci. Non odio euismod lacinia at quis risus. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Ut faucibus pulvinar elementum integer enim neque volutpat ac. A lacus vestibulum sed arcu non odio euismod. Nec feugiat in fermentum posuere urna nec. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt. Urna et pharetra pharetra massa massa ultricies mi quis. Ut eu sem integer vitae justo eget magna fermentum iaculis. Eros in cursus turpis massa tincidunt. Ullamcorper morbi tincidunt ornare massa. Tristique magna sit amet purus gravida quis.
|
||||||
|
|
||||||
|
Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Tortor condimentum lacinia quis vel eros donec ac odio. Duis at consectetur lorem donec massa sapien faucibus et molestie. Viverra ipsum nunc aliquet bibendum enim facilisis gravida neque. Viverra maecenas accumsan lacus vel facilisis. Ipsum dolor sit amet consectetur adipiscing elit pellentesque. Sollicitudin aliquam ultrices sagittis orci a scelerisque purus semper eget. Ullamcorper malesuada proin libero nunc. Amet nulla facilisi morbi tempus iaculis urna id. Ullamcorper malesuada proin libero nunc consequat interdum varius sit. Bibendum neque egestas congue quisque egestas diam in arcu cursus. Eget gravida cum sociis natoque penatibus. Non sodales neque sodales ut etiam sit amet nisl. Bibendum ut tristique et egestas. Felis bibendum ut tristique et egestas quis ipsum suspendisse ultrices. Quis auctor elit sed vulputate mi sit.
|
||||||
|
|
||||||
|
Ut morbi tincidunt augue interdum velit euismod in pellentesque massa. Sagittis id consectetur purus ut faucibus pulvinar elementum. Et tortor at risus viverra adipiscing at. Vitae auctor eu augue ut lectus. Egestas pretium aenean pharetra magna. Ante in nibh mauris cursus mattis. Nisl condimentum id venenatis a condimentum vitae sapien pellentesque. Dis parturient montes nascetur ridiculus mus mauris vitae. Aliquam faucibus purus in massa tempor nec. Bibendum at varius vel pharetra vel turpis. Tellus pellentesque eu tincidunt tortor aliquam nulla. Laoreet sit amet cursus sit.</div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'portfolio-site',
|
||||||
|
title: 'Portfolio Website',
|
||||||
|
tags: ['React', 'JavaScript', 'Web Dev'],
|
||||||
|
imgSrc: 'https://cdn.pixabay.com/photo/2023/03/06/17/02/ship-7833921_1280.jpg',
|
||||||
|
imgPOI: { x: 0, y: -140 },
|
||||||
|
desc: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Odio morbi quis commodo odio aenean sed. Id porta nibh venenatis cras sed felis eget velit aliquet. Varius sit amet mattis vulputate enim nulla. ',
|
||||||
|
content: <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Blandit cursus risus at ultrices mi tempus imperdiet nulla malesuada. Fusce ut placerat orci nulla pellentesque dignissim enim sit. Lorem ipsum dolor sit amet consectetur. Convallis aenean et tortor at risus viverra adipiscing at in. Libero id faucibus nisl tincidunt eget. Proin nibh nisl condimentum id venenatis. Nibh sit amet commodo nulla. Suscipit tellus mauris a diam maecenas sed enim. Malesuada pellentesque elit eget gravida cum sociis natoque penatibus. Pulvinar sapien et ligula ullamcorper malesuada proin libero. Nunc consequat interdum varius sit amet mattis vulputate enim nulla. Nunc aliquet bibendum enim facilisis gravida neque convallis a cras. Placerat duis ultricies lacus sed turpis. Malesuada bibendum arcu vitae elementum curabitur vitae nunc sed. Massa ultricies mi quis hendrerit dolor magna eget est. Eu sem integer vitae justo eget magna. Orci eu lobortis elementum nibh. Tempus imperdiet nulla malesuada pellentesque elit eget gravida. Diam sollicitudin tempor id eu nisl nunc mi ipsum.
|
||||||
|
|
||||||
|
Eu nisl nunc mi ipsum faucibus vitae. Pharetra massa massa ultricies mi quis hendrerit. Turpis in eu mi bibendum. Nibh nisl condimentum id venenatis a. Sapien pellentesque habitant morbi tristique. Pharetra diam sit amet nisl. Mi in nulla posuere sollicitudin aliquam ultrices. Maecenas volutpat blandit aliquam etiam. Suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Cras ornare arcu dui vivamus. Ut consequat semper viverra nam. Auctor neque vitae tempus quam pellentesque nec nam. Quis blandit turpis cursus in hac habitasse. Congue eu consequat ac felis donec. Urna condimentum mattis pellentesque id nibh tortor id aliquet.
|
||||||
|
|
||||||
|
Cras pulvinar mattis nunc sed blandit libero volutpat. Nunc sed augue lacus viverra vitae congue eu. Proin libero nunc consequat interdum varius sit. Risus pretium quam vulputate dignissim suspendisse in. In est ante in nibh mauris cursus. Lacinia quis vel eros donec ac odio tempor orci. Non odio euismod lacinia at quis risus. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Ut faucibus pulvinar elementum integer enim neque volutpat ac. A lacus vestibulum sed arcu non odio euismod. Nec feugiat in fermentum posuere urna nec. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt. Urna et pharetra pharetra massa massa ultricies mi quis. Ut eu sem integer vitae justo eget magna fermentum iaculis. Eros in cursus turpis massa tincidunt. Ullamcorper morbi tincidunt ornare massa. Tristique magna sit amet purus gravida quis.
|
||||||
|
|
||||||
|
Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Tortor condimentum lacinia quis vel eros donec ac odio. Duis at consectetur lorem donec massa sapien faucibus et molestie. Viverra ipsum nunc aliquet bibendum enim facilisis gravida neque. Viverra maecenas accumsan lacus vel facilisis. Ipsum dolor sit amet consectetur adipiscing elit pellentesque. Sollicitudin aliquam ultrices sagittis orci a scelerisque purus semper eget. Ullamcorper malesuada proin libero nunc. Amet nulla facilisi morbi tempus iaculis urna id. Ullamcorper malesuada proin libero nunc consequat interdum varius sit. Bibendum neque egestas congue quisque egestas diam in arcu cursus. Eget gravida cum sociis natoque penatibus. Non sodales neque sodales ut etiam sit amet nisl. Bibendum ut tristique et egestas. Felis bibendum ut tristique et egestas quis ipsum suspendisse ultrices. Quis auctor elit sed vulputate mi sit.
|
||||||
|
|
||||||
|
Ut morbi tincidunt augue interdum velit euismod in pellentesque massa. Sagittis id consectetur purus ut faucibus pulvinar elementum. Et tortor at risus viverra adipiscing at. Vitae auctor eu augue ut lectus. Egestas pretium aenean pharetra magna. Ante in nibh mauris cursus mattis. Nisl condimentum id venenatis a condimentum vitae sapien pellentesque. Dis parturient montes nascetur ridiculus mus mauris vitae. Aliquam faucibus purus in massa tempor nec. Bibendum at varius vel pharetra vel turpis. Tellus pellentesque eu tincidunt tortor aliquam nulla. Laoreet sit amet cursus sit.</div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'project-3',
|
||||||
|
title: 'Project 3',
|
||||||
|
tags: ['one', 'two', 'three'],
|
||||||
|
imgSrc: 'https://cdn.pixabay.com/photo/2023/08/15/11/47/mushroom-8191823_1280.jpg',
|
||||||
|
imgPOI: { x: 0, y: 0 },
|
||||||
|
desc: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Odio morbi quis commodo odio aenean sed. Id porta nibh venenatis cras sed felis eget velit aliquet. Varius sit amet mattis vulputate enim nulla. ',
|
||||||
|
content: <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Blandit cursus risus at ultrices mi tempus imperdiet nulla malesuada. Fusce ut placerat orci nulla pellentesque dignissim enim sit. Lorem ipsum dolor sit amet consectetur. Convallis aenean et tortor at risus viverra adipiscing at in. Libero id faucibus nisl tincidunt eget. Proin nibh nisl condimentum id venenatis. Nibh sit amet commodo nulla. Suscipit tellus mauris a diam maecenas sed enim. Malesuada pellentesque elit eget gravida cum sociis natoque penatibus. Pulvinar sapien et ligula ullamcorper malesuada proin libero. Nunc consequat interdum varius sit amet mattis vulputate enim nulla. Nunc aliquet bibendum enim facilisis gravida neque convallis a cras. Placerat duis ultricies lacus sed turpis. Malesuada bibendum arcu vitae elementum curabitur vitae nunc sed. Massa ultricies mi quis hendrerit dolor magna eget est. Eu sem integer vitae justo eget magna. Orci eu lobortis elementum nibh. Tempus imperdiet nulla malesuada pellentesque elit eget gravida. Diam sollicitudin tempor id eu nisl nunc mi ipsum.
|
||||||
|
|
||||||
|
Eu nisl nunc mi ipsum faucibus vitae. Pharetra massa massa ultricies mi quis hendrerit. Turpis in eu mi bibendum. Nibh nisl condimentum id venenatis a. Sapien pellentesque habitant morbi tristique. Pharetra diam sit amet nisl. Mi in nulla posuere sollicitudin aliquam ultrices. Maecenas volutpat blandit aliquam etiam. Suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Cras ornare arcu dui vivamus. Ut consequat semper viverra nam. Auctor neque vitae tempus quam pellentesque nec nam. Quis blandit turpis cursus in hac habitasse. Congue eu consequat ac felis donec. Urna condimentum mattis pellentesque id nibh tortor id aliquet.
|
||||||
|
|
||||||
|
Cras pulvinar mattis nunc sed blandit libero volutpat. Nunc sed augue lacus viverra vitae congue eu. Proin libero nunc consequat interdum varius sit. Risus pretium quam vulputate dignissim suspendisse in. In est ante in nibh mauris cursus. Lacinia quis vel eros donec ac odio tempor orci. Non odio euismod lacinia at quis risus. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Ut faucibus pulvinar elementum integer enim neque volutpat ac. A lacus vestibulum sed arcu non odio euismod. Nec feugiat in fermentum posuere urna nec. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt. Urna et pharetra pharetra massa massa ultricies mi quis. Ut eu sem integer vitae justo eget magna fermentum iaculis. Eros in cursus turpis massa tincidunt. Ullamcorper morbi tincidunt ornare massa. Tristique magna sit amet purus gravida quis.
|
||||||
|
|
||||||
|
Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Tortor condimentum lacinia quis vel eros donec ac odio. Duis at consectetur lorem donec massa sapien faucibus et molestie. Viverra ipsum nunc aliquet bibendum enim facilisis gravida neque. Viverra maecenas accumsan lacus vel facilisis. Ipsum dolor sit amet consectetur adipiscing elit pellentesque. Sollicitudin aliquam ultrices sagittis orci a scelerisque purus semper eget. Ullamcorper malesuada proin libero nunc. Amet nulla facilisi morbi tempus iaculis urna id. Ullamcorper malesuada proin libero nunc consequat interdum varius sit. Bibendum neque egestas congue quisque egestas diam in arcu cursus. Eget gravida cum sociis natoque penatibus. Non sodales neque sodales ut etiam sit amet nisl. Bibendum ut tristique et egestas. Felis bibendum ut tristique et egestas quis ipsum suspendisse ultrices. Quis auctor elit sed vulputate mi sit.
|
||||||
|
|
||||||
|
Ut morbi tincidunt augue interdum velit euismod in pellentesque massa. Sagittis id consectetur purus ut faucibus pulvinar elementum. Et tortor at risus viverra adipiscing at. Vitae auctor eu augue ut lectus. Egestas pretium aenean pharetra magna. Ante in nibh mauris cursus mattis. Nisl condimentum id venenatis a condimentum vitae sapien pellentesque. Dis parturient montes nascetur ridiculus mus mauris vitae. Aliquam faucibus purus in massa tempor nec. Bibendum at varius vel pharetra vel turpis. Tellus pellentesque eu tincidunt tortor aliquam nulla. Laoreet sit amet cursus sit.</div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'project-4',
|
||||||
|
title: 'Project 4',
|
||||||
|
tags: ['one', 'two', 'three'],
|
||||||
|
imgSrc: 'https://cdn.pixabay.com/photo/2023/08/11/05/44/ai-generated-8182842_1280.jpg',
|
||||||
|
imgPOI: { x: 0, y: 0 },
|
||||||
|
desc: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Odio morbi quis commodo odio aenean sed. Id porta nibh venenatis cras sed felis eget velit aliquet. Varius sit amet mattis vulputate enim nulla. ',
|
||||||
|
content: <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Blandit cursus risus at ultrices mi tempus imperdiet nulla malesuada. Fusce ut placerat orci nulla pellentesque dignissim enim sit. Lorem ipsum dolor sit amet consectetur. Convallis aenean et tortor at risus viverra adipiscing at in. Libero id faucibus nisl tincidunt eget. Proin nibh nisl condimentum id venenatis. Nibh sit amet commodo nulla. Suscipit tellus mauris a diam maecenas sed enim. Malesuada pellentesque elit eget gravida cum sociis natoque penatibus. Pulvinar sapien et ligula ullamcorper malesuada proin libero. Nunc consequat interdum varius sit amet mattis vulputate enim nulla. Nunc aliquet bibendum enim facilisis gravida neque convallis a cras. Placerat duis ultricies lacus sed turpis. Malesuada bibendum arcu vitae elementum curabitur vitae nunc sed. Massa ultricies mi quis hendrerit dolor magna eget est. Eu sem integer vitae justo eget magna. Orci eu lobortis elementum nibh. Tempus imperdiet nulla malesuada pellentesque elit eget gravida. Diam sollicitudin tempor id eu nisl nunc mi ipsum.
|
||||||
|
|
||||||
|
Eu nisl nunc mi ipsum faucibus vitae. Pharetra massa massa ultricies mi quis hendrerit. Turpis in eu mi bibendum. Nibh nisl condimentum id venenatis a. Sapien pellentesque habitant morbi tristique. Pharetra diam sit amet nisl. Mi in nulla posuere sollicitudin aliquam ultrices. Maecenas volutpat blandit aliquam etiam. Suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Cras ornare arcu dui vivamus. Ut consequat semper viverra nam. Auctor neque vitae tempus quam pellentesque nec nam. Quis blandit turpis cursus in hac habitasse. Congue eu consequat ac felis donec. Urna condimentum mattis pellentesque id nibh tortor id aliquet.
|
||||||
|
|
||||||
|
Cras pulvinar mattis nunc sed blandit libero volutpat. Nunc sed augue lacus viverra vitae congue eu. Proin libero nunc consequat interdum varius sit. Risus pretium quam vulputate dignissim suspendisse in. In est ante in nibh mauris cursus. Lacinia quis vel eros donec ac odio tempor orci. Non odio euismod lacinia at quis risus. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Ut faucibus pulvinar elementum integer enim neque volutpat ac. A lacus vestibulum sed arcu non odio euismod. Nec feugiat in fermentum posuere urna nec. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt. Urna et pharetra pharetra massa massa ultricies mi quis. Ut eu sem integer vitae justo eget magna fermentum iaculis. Eros in cursus turpis massa tincidunt. Ullamcorper morbi tincidunt ornare massa. Tristique magna sit amet purus gravida quis.
|
||||||
|
|
||||||
|
Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Tortor condimentum lacinia quis vel eros donec ac odio. Duis at consectetur lorem donec massa sapien faucibus et molestie. Viverra ipsum nunc aliquet bibendum enim facilisis gravida neque. Viverra maecenas accumsan lacus vel facilisis. Ipsum dolor sit amet consectetur adipiscing elit pellentesque. Sollicitudin aliquam ultrices sagittis orci a scelerisque purus semper eget. Ullamcorper malesuada proin libero nunc. Amet nulla facilisi morbi tempus iaculis urna id. Ullamcorper malesuada proin libero nunc consequat interdum varius sit. Bibendum neque egestas congue quisque egestas diam in arcu cursus. Eget gravida cum sociis natoque penatibus. Non sodales neque sodales ut etiam sit amet nisl. Bibendum ut tristique et egestas. Felis bibendum ut tristique et egestas quis ipsum suspendisse ultrices. Quis auctor elit sed vulputate mi sit.
|
||||||
|
|
||||||
|
Ut morbi tincidunt augue interdum velit euismod in pellentesque massa. Sagittis id consectetur purus ut faucibus pulvinar elementum. Et tortor at risus viverra adipiscing at. Vitae auctor eu augue ut lectus. Egestas pretium aenean pharetra magna. Ante in nibh mauris cursus mattis. Nisl condimentum id venenatis a condimentum vitae sapien pellentesque. Dis parturient montes nascetur ridiculus mus mauris vitae. Aliquam faucibus purus in massa tempor nec. Bibendum at varius vel pharetra vel turpis. Tellus pellentesque eu tincidunt tortor aliquam nulla. Laoreet sit amet cursus sit.</div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'project-5',
|
||||||
|
title: 'Project 5',
|
||||||
|
tags: ['one', 'two', 'three'],
|
||||||
|
imgSrc: 'https://cdn.pixabay.com/photo/2023/05/01/06/17/pelican-7962189_1280.jpg',
|
||||||
|
imgPOI: { x: 0, y: 0 },
|
||||||
|
desc: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Odio morbi quis commodo odio aenean sed. Id porta nibh venenatis cras sed felis eget velit aliquet. Varius sit amet mattis vulputate enim nulla. ',
|
||||||
|
content: <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Blandit cursus risus at ultrices mi tempus imperdiet nulla malesuada. Fusce ut placerat orci nulla pellentesque dignissim enim sit. Lorem ipsum dolor sit amet consectetur. Convallis aenean et tortor at risus viverra adipiscing at in. Libero id faucibus nisl tincidunt eget. Proin nibh nisl condimentum id venenatis. Nibh sit amet commodo nulla. Suscipit tellus mauris a diam maecenas sed enim. Malesuada pellentesque elit eget gravida cum sociis natoque penatibus. Pulvinar sapien et ligula ullamcorper malesuada proin libero. Nunc consequat interdum varius sit amet mattis vulputate enim nulla. Nunc aliquet bibendum enim facilisis gravida neque convallis a cras. Placerat duis ultricies lacus sed turpis. Malesuada bibendum arcu vitae elementum curabitur vitae nunc sed. Massa ultricies mi quis hendrerit dolor magna eget est. Eu sem integer vitae justo eget magna. Orci eu lobortis elementum nibh. Tempus imperdiet nulla malesuada pellentesque elit eget gravida. Diam sollicitudin tempor id eu nisl nunc mi ipsum.
|
||||||
|
|
||||||
|
Eu nisl nunc mi ipsum faucibus vitae. Pharetra massa massa ultricies mi quis hendrerit. Turpis in eu mi bibendum. Nibh nisl condimentum id venenatis a. Sapien pellentesque habitant morbi tristique. Pharetra diam sit amet nisl. Mi in nulla posuere sollicitudin aliquam ultrices. Maecenas volutpat blandit aliquam etiam. Suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Cras ornare arcu dui vivamus. Ut consequat semper viverra nam. Auctor neque vitae tempus quam pellentesque nec nam. Quis blandit turpis cursus in hac habitasse. Congue eu consequat ac felis donec. Urna condimentum mattis pellentesque id nibh tortor id aliquet.
|
||||||
|
|
||||||
|
Cras pulvinar mattis nunc sed blandit libero volutpat. Nunc sed augue lacus viverra vitae congue eu. Proin libero nunc consequat interdum varius sit. Risus pretium quam vulputate dignissim suspendisse in. In est ante in nibh mauris cursus. Lacinia quis vel eros donec ac odio tempor orci. Non odio euismod lacinia at quis risus. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Ut faucibus pulvinar elementum integer enim neque volutpat ac. A lacus vestibulum sed arcu non odio euismod. Nec feugiat in fermentum posuere urna nec. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt. Urna et pharetra pharetra massa massa ultricies mi quis. Ut eu sem integer vitae justo eget magna fermentum iaculis. Eros in cursus turpis massa tincidunt. Ullamcorper morbi tincidunt ornare massa. Tristique magna sit amet purus gravida quis.
|
||||||
|
|
||||||
|
Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Tortor condimentum lacinia quis vel eros donec ac odio. Duis at consectetur lorem donec massa sapien faucibus et molestie. Viverra ipsum nunc aliquet bibendum enim facilisis gravida neque. Viverra maecenas accumsan lacus vel facilisis. Ipsum dolor sit amet consectetur adipiscing elit pellentesque. Sollicitudin aliquam ultrices sagittis orci a scelerisque purus semper eget. Ullamcorper malesuada proin libero nunc. Amet nulla facilisi morbi tempus iaculis urna id. Ullamcorper malesuada proin libero nunc consequat interdum varius sit. Bibendum neque egestas congue quisque egestas diam in arcu cursus. Eget gravida cum sociis natoque penatibus. Non sodales neque sodales ut etiam sit amet nisl. Bibendum ut tristique et egestas. Felis bibendum ut tristique et egestas quis ipsum suspendisse ultrices. Quis auctor elit sed vulputate mi sit.
|
||||||
|
|
||||||
|
Ut morbi tincidunt augue interdum velit euismod in pellentesque massa. Sagittis id consectetur purus ut faucibus pulvinar elementum. Et tortor at risus viverra adipiscing at. Vitae auctor eu augue ut lectus. Egestas pretium aenean pharetra magna. Ante in nibh mauris cursus mattis. Nisl condimentum id venenatis a condimentum vitae sapien pellentesque. Dis parturient montes nascetur ridiculus mus mauris vitae. Aliquam faucibus purus in massa tempor nec. Bibendum at varius vel pharetra vel turpis. Tellus pellentesque eu tincidunt tortor aliquam nulla. Laoreet sit amet cursus sit.</div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'project-6',
|
||||||
|
title: 'Project 6',
|
||||||
|
tags: ['one', 'two', 'three'],
|
||||||
|
imgSrc: 'https://cdn.pixabay.com/photo/2023/05/01/06/17/pelican-7962189_1280.jpg',
|
||||||
|
imgPOI: { x: 0, y: 0 },
|
||||||
|
desc: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Odio morbi quis commodo odio aenean sed. Id porta nibh venenatis cras sed felis eget velit aliquet. Varius sit amet mattis vulputate enim nulla. ',
|
||||||
|
content: <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Blandit cursus risus at ultrices mi tempus imperdiet nulla malesuada. Fusce ut placerat orci nulla pellentesque dignissim enim sit. Lorem ipsum dolor sit amet consectetur. Convallis aenean et tortor at risus viverra adipiscing at in. Libero id faucibus nisl tincidunt eget. Proin nibh nisl condimentum id venenatis. Nibh sit amet commodo nulla. Suscipit tellus mauris a diam maecenas sed enim. Malesuada pellentesque elit eget gravida cum sociis natoque penatibus. Pulvinar sapien et ligula ullamcorper malesuada proin libero. Nunc consequat interdum varius sit amet mattis vulputate enim nulla. Nunc aliquet bibendum enim facilisis gravida neque convallis a cras. Placerat duis ultricies lacus sed turpis. Malesuada bibendum arcu vitae elementum curabitur vitae nunc sed. Massa ultricies mi quis hendrerit dolor magna eget est. Eu sem integer vitae justo eget magna. Orci eu lobortis elementum nibh. Tempus imperdiet nulla malesuada pellentesque elit eget gravida. Diam sollicitudin tempor id eu nisl nunc mi ipsum.
|
||||||
|
|
||||||
|
Eu nisl nunc mi ipsum faucibus vitae. Pharetra massa massa ultricies mi quis hendrerit. Turpis in eu mi bibendum. Nibh nisl condimentum id venenatis a. Sapien pellentesque habitant morbi tristique. Pharetra diam sit amet nisl. Mi in nulla posuere sollicitudin aliquam ultrices. Maecenas volutpat blandit aliquam etiam. Suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Cras ornare arcu dui vivamus. Ut consequat semper viverra nam. Auctor neque vitae tempus quam pellentesque nec nam. Quis blandit turpis cursus in hac habitasse. Congue eu consequat ac felis donec. Urna condimentum mattis pellentesque id nibh tortor id aliquet.
|
||||||
|
|
||||||
|
Cras pulvinar mattis nunc sed blandit libero volutpat. Nunc sed augue lacus viverra vitae congue eu. Proin libero nunc consequat interdum varius sit. Risus pretium quam vulputate dignissim suspendisse in. In est ante in nibh mauris cursus. Lacinia quis vel eros donec ac odio tempor orci. Non odio euismod lacinia at quis risus. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Ut faucibus pulvinar elementum integer enim neque volutpat ac. A lacus vestibulum sed arcu non odio euismod. Nec feugiat in fermentum posuere urna nec. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt. Urna et pharetra pharetra massa massa ultricies mi quis. Ut eu sem integer vitae justo eget magna fermentum iaculis. Eros in cursus turpis massa tincidunt. Ullamcorper morbi tincidunt ornare massa. Tristique magna sit amet purus gravida quis.
|
||||||
|
|
||||||
|
Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Tortor condimentum lacinia quis vel eros donec ac odio. Duis at consectetur lorem donec massa sapien faucibus et molestie. Viverra ipsum nunc aliquet bibendum enim facilisis gravida neque. Viverra maecenas accumsan lacus vel facilisis. Ipsum dolor sit amet consectetur adipiscing elit pellentesque. Sollicitudin aliquam ultrices sagittis orci a scelerisque purus semper eget. Ullamcorper malesuada proin libero nunc. Amet nulla facilisi morbi tempus iaculis urna id. Ullamcorper malesuada proin libero nunc consequat interdum varius sit. Bibendum neque egestas congue quisque egestas diam in arcu cursus. Eget gravida cum sociis natoque penatibus. Non sodales neque sodales ut etiam sit amet nisl. Bibendum ut tristique et egestas. Felis bibendum ut tristique et egestas quis ipsum suspendisse ultrices. Quis auctor elit sed vulputate mi sit.
|
||||||
|
|
||||||
|
Ut morbi tincidunt augue interdum velit euismod in pellentesque massa. Sagittis id consectetur purus ut faucibus pulvinar elementum. Et tortor at risus viverra adipiscing at. Vitae auctor eu augue ut lectus. Egestas pretium aenean pharetra magna. Ante in nibh mauris cursus mattis. Nisl condimentum id venenatis a condimentum vitae sapien pellentesque. Dis parturient montes nascetur ridiculus mus mauris vitae. Aliquam faucibus purus in massa tempor nec. Bibendum at varius vel pharetra vel turpis. Tellus pellentesque eu tincidunt tortor aliquam nulla. Laoreet sit amet cursus sit.</div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'project-7',
|
||||||
|
title: 'Project 7',
|
||||||
|
tags: ['one', 'two', 'three'],
|
||||||
|
imgSrc: 'https://cdn.pixabay.com/photo/2023/05/01/06/17/pelican-7962189_1280.jpg',
|
||||||
|
imgPOI: { x: 0, y: 0 },
|
||||||
|
desc: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Odio morbi quis commodo odio aenean sed. Id porta nibh venenatis cras sed felis eget velit aliquet. Varius sit amet mattis vulputate enim nulla. ',
|
||||||
|
content: <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Blandit cursus risus at ultrices mi tempus imperdiet nulla malesuada. Fusce ut placerat orci nulla pellentesque dignissim enim sit. Lorem ipsum dolor sit amet consectetur. Convallis aenean et tortor at risus viverra adipiscing at in. Libero id faucibus nisl tincidunt eget. Proin nibh nisl condimentum id venenatis. Nibh sit amet commodo nulla. Suscipit tellus mauris a diam maecenas sed enim. Malesuada pellentesque elit eget gravida cum sociis natoque penatibus. Pulvinar sapien et ligula ullamcorper malesuada proin libero. Nunc consequat interdum varius sit amet mattis vulputate enim nulla. Nunc aliquet bibendum enim facilisis gravida neque convallis a cras. Placerat duis ultricies lacus sed turpis. Malesuada bibendum arcu vitae elementum curabitur vitae nunc sed. Massa ultricies mi quis hendrerit dolor magna eget est. Eu sem integer vitae justo eget magna. Orci eu lobortis elementum nibh. Tempus imperdiet nulla malesuada pellentesque elit eget gravida. Diam sollicitudin tempor id eu nisl nunc mi ipsum.
|
||||||
|
|
||||||
|
Eu nisl nunc mi ipsum faucibus vitae. Pharetra massa massa ultricies mi quis hendrerit. Turpis in eu mi bibendum. Nibh nisl condimentum id venenatis a. Sapien pellentesque habitant morbi tristique. Pharetra diam sit amet nisl. Mi in nulla posuere sollicitudin aliquam ultrices. Maecenas volutpat blandit aliquam etiam. Suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Cras ornare arcu dui vivamus. Ut consequat semper viverra nam. Auctor neque vitae tempus quam pellentesque nec nam. Quis blandit turpis cursus in hac habitasse. Congue eu consequat ac felis donec. Urna condimentum mattis pellentesque id nibh tortor id aliquet.
|
||||||
|
|
||||||
|
Cras pulvinar mattis nunc sed blandit libero volutpat. Nunc sed augue lacus viverra vitae congue eu. Proin libero nunc consequat interdum varius sit. Risus pretium quam vulputate dignissim suspendisse in. In est ante in nibh mauris cursus. Lacinia quis vel eros donec ac odio tempor orci. Non odio euismod lacinia at quis risus. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Ut faucibus pulvinar elementum integer enim neque volutpat ac. A lacus vestibulum sed arcu non odio euismod. Nec feugiat in fermentum posuere urna nec. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt. Urna et pharetra pharetra massa massa ultricies mi quis. Ut eu sem integer vitae justo eget magna fermentum iaculis. Eros in cursus turpis massa tincidunt. Ullamcorper morbi tincidunt ornare massa. Tristique magna sit amet purus gravida quis.
|
||||||
|
|
||||||
|
Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Tortor condimentum lacinia quis vel eros donec ac odio. Duis at consectetur lorem donec massa sapien faucibus et molestie. Viverra ipsum nunc aliquet bibendum enim facilisis gravida neque. Viverra maecenas accumsan lacus vel facilisis. Ipsum dolor sit amet consectetur adipiscing elit pellentesque. Sollicitudin aliquam ultrices sagittis orci a scelerisque purus semper eget. Ullamcorper malesuada proin libero nunc. Amet nulla facilisi morbi tempus iaculis urna id. Ullamcorper malesuada proin libero nunc consequat interdum varius sit. Bibendum neque egestas congue quisque egestas diam in arcu cursus. Eget gravida cum sociis natoque penatibus. Non sodales neque sodales ut etiam sit amet nisl. Bibendum ut tristique et egestas. Felis bibendum ut tristique et egestas quis ipsum suspendisse ultrices. Quis auctor elit sed vulputate mi sit.
|
||||||
|
|
||||||
|
Ut morbi tincidunt augue interdum velit euismod in pellentesque massa. Sagittis id consectetur purus ut faucibus pulvinar elementum. Et tortor at risus viverra adipiscing at. Vitae auctor eu augue ut lectus. Egestas pretium aenean pharetra magna. Ante in nibh mauris cursus mattis. Nisl condimentum id venenatis a condimentum vitae sapien pellentesque. Dis parturient montes nascetur ridiculus mus mauris vitae. Aliquam faucibus purus in massa tempor nec. Bibendum at varius vel pharetra vel turpis. Tellus pellentesque eu tincidunt tortor aliquam nulla. Laoreet sit amet cursus sit.</div>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'project-8',
|
||||||
|
title: 'Project 8',
|
||||||
|
tags: ['one', 'two', 'three'],
|
||||||
|
imgSrc: 'https://cdn.pixabay.com/photo/2023/05/01/06/17/pelican-7962189_1280.jpg',
|
||||||
|
imgPOI: { x: 0, y: 0 },
|
||||||
|
desc: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Odio morbi quis commodo odio aenean sed. Id porta nibh venenatis cras sed felis eget velit aliquet. Varius sit amet mattis vulputate enim nulla. ',
|
||||||
|
content: <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Blandit cursus risus at ultrices mi tempus imperdiet nulla malesuada. Fusce ut placerat orci nulla pellentesque dignissim enim sit. Lorem ipsum dolor sit amet consectetur. Convallis aenean et tortor at risus viverra adipiscing at in. Libero id faucibus nisl tincidunt eget. Proin nibh nisl condimentum id venenatis. Nibh sit amet commodo nulla. Suscipit tellus mauris a diam maecenas sed enim. Malesuada pellentesque elit eget gravida cum sociis natoque penatibus. Pulvinar sapien et ligula ullamcorper malesuada proin libero. Nunc consequat interdum varius sit amet mattis vulputate enim nulla. Nunc aliquet bibendum enim facilisis gravida neque convallis a cras. Placerat duis ultricies lacus sed turpis. Malesuada bibendum arcu vitae elementum curabitur vitae nunc sed. Massa ultricies mi quis hendrerit dolor magna eget est. Eu sem integer vitae justo eget magna. Orci eu lobortis elementum nibh. Tempus imperdiet nulla malesuada pellentesque elit eget gravida. Diam sollicitudin tempor id eu nisl nunc mi ipsum.
|
||||||
|
|
||||||
|
Eu nisl nunc mi ipsum faucibus vitae. Pharetra massa massa ultricies mi quis hendrerit. Turpis in eu mi bibendum. Nibh nisl condimentum id venenatis a. Sapien pellentesque habitant morbi tristique. Pharetra diam sit amet nisl. Mi in nulla posuere sollicitudin aliquam ultrices. Maecenas volutpat blandit aliquam etiam. Suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Cras ornare arcu dui vivamus. Ut consequat semper viverra nam. Auctor neque vitae tempus quam pellentesque nec nam. Quis blandit turpis cursus in hac habitasse. Congue eu consequat ac felis donec. Urna condimentum mattis pellentesque id nibh tortor id aliquet.
|
||||||
|
|
||||||
|
Cras pulvinar mattis nunc sed blandit libero volutpat. Nunc sed augue lacus viverra vitae congue eu. Proin libero nunc consequat interdum varius sit. Risus pretium quam vulputate dignissim suspendisse in. In est ante in nibh mauris cursus. Lacinia quis vel eros donec ac odio tempor orci. Non odio euismod lacinia at quis risus. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Ut faucibus pulvinar elementum integer enim neque volutpat ac. A lacus vestibulum sed arcu non odio euismod. Nec feugiat in fermentum posuere urna nec. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt. Urna et pharetra pharetra massa massa ultricies mi quis. Ut eu sem integer vitae justo eget magna fermentum iaculis. Eros in cursus turpis massa tincidunt. Ullamcorper morbi tincidunt ornare massa. Tristique magna sit amet purus gravida quis.
|
||||||
|
|
||||||
|
Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi. Cras ornare arcu dui vivamus arcu felis bibendum ut tristique. Tortor condimentum lacinia quis vel eros donec ac odio. Duis at consectetur lorem donec massa sapien faucibus et molestie. Viverra ipsum nunc aliquet bibendum enim facilisis gravida neque. Viverra maecenas accumsan lacus vel facilisis. Ipsum dolor sit amet consectetur adipiscing elit pellentesque. Sollicitudin aliquam ultrices sagittis orci a scelerisque purus semper eget. Ullamcorper malesuada proin libero nunc. Amet nulla facilisi morbi tempus iaculis urna id. Ullamcorper malesuada proin libero nunc consequat interdum varius sit. Bibendum neque egestas congue quisque egestas diam in arcu cursus. Eget gravida cum sociis natoque penatibus. Non sodales neque sodales ut etiam sit amet nisl. Bibendum ut tristique et egestas. Felis bibendum ut tristique et egestas quis ipsum suspendisse ultrices. Quis auctor elit sed vulputate mi sit.
|
||||||
|
|
||||||
|
Ut morbi tincidunt augue interdum velit euismod in pellentesque massa. Sagittis id consectetur purus ut faucibus pulvinar elementum. Et tortor at risus viverra adipiscing at. Vitae auctor eu augue ut lectus. Egestas pretium aenean pharetra magna. Ante in nibh mauris cursus mattis. Nisl condimentum id venenatis a condimentum vitae sapien pellentesque. Dis parturient montes nascetur ridiculus mus mauris vitae. Aliquam faucibus purus in massa tempor nec. Bibendum at varius vel pharetra vel turpis. Tellus pellentesque eu tincidunt tortor aliquam nulla. Laoreet sit amet cursus sit.</div>
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default projectsData
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import Path from '@joemaddalone/path'
|
||||||
|
|
||||||
|
function randRange (min, max) {
|
||||||
|
return Math.random() * (max - min) + min
|
||||||
|
}
|
||||||
|
|
||||||
|
const useBuildBottomCurvePath = (numPoints, width, height, curveHeight) => {
|
||||||
|
const curveStart = {
|
||||||
|
x: 0, y: randRange(0, curveHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
const curve = []
|
||||||
|
|
||||||
|
let prev = {
|
||||||
|
cx1: undefined,
|
||||||
|
cy1: undefined,
|
||||||
|
cx2: undefined,
|
||||||
|
cy2: undefined,
|
||||||
|
ex: curveStart.x,
|
||||||
|
ey: curveStart.y
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < numPoints; i++) {
|
||||||
|
const cy2 = randRange(0, curveHeight)
|
||||||
|
curve.push({
|
||||||
|
cx1: prev.ex + width / numPoints / 3,
|
||||||
|
cy1: prev.cy1 ? prev.ey + (prev.ey - prev.cy2) : randRange(0, curveHeight),
|
||||||
|
cx2: prev.ex + width / numPoints * 2 / 3,
|
||||||
|
cy2,
|
||||||
|
ex: prev.ex + width / numPoints,
|
||||||
|
ey: randRange(cy2, curveHeight)
|
||||||
|
})
|
||||||
|
prev = curve[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = new Path()
|
||||||
|
path.moveTo(width, height)
|
||||||
|
path.L(0, height)
|
||||||
|
path.L(curveStart.x, curveStart.y)
|
||||||
|
curve.forEach(curve => path.C(curve.cx1, curve.cy1, curve.cx2, curve.cy2, curve.ex, curve.ey))
|
||||||
|
path.L(width, height)
|
||||||
|
|
||||||
|
return path.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useBuildBottomCurvePath
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import Color from '../Classes/Color'
|
||||||
|
import useBuildTopCurvePath from '../Hooks/useBuildTopCurvePath'
|
||||||
|
import useBuildBottomCurvePath from '../Hooks/useBuildBottomCurvePath'
|
||||||
|
|
||||||
|
const useBuildCurves = (num, curveHeight, topColor, bottomColor, bgColor) => {
|
||||||
|
const curves = { tops: [], bottoms: [] }
|
||||||
|
|
||||||
|
for (let i = num - 1; i >= 0; i--) {
|
||||||
|
const tColor = Color.blend(topColor, bgColor, i === 0 ? 0 : computeLerpConst2(i / num))
|
||||||
|
const bColor = Color.blend(bottomColor, bgColor, i === 0 ? 0 : computeLerpConst2(i / num))
|
||||||
|
curves.tops.push({
|
||||||
|
path: useBuildTopCurvePath(12, 100, 100, curveHeight - 1),
|
||||||
|
color: tColor.toString(),
|
||||||
|
motionVariants: {
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
pathLength: 0,
|
||||||
|
fill: tColor.set({ a: 0 }).toString()
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
pathLength: 1,
|
||||||
|
fill: tColor.set({ a: 1 }).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
curves.bottoms.push({
|
||||||
|
path: useBuildBottomCurvePath(12, 100, 100, curveHeight - 1),
|
||||||
|
color: bColor.toString(),
|
||||||
|
motionVariants: {
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
pathLength: 0,
|
||||||
|
fill: tColor.set({ a: 0 }).toString()
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
pathLength: 1,
|
||||||
|
fill: tColor.set({ a: 1 }).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return curves
|
||||||
|
}
|
||||||
|
|
||||||
|
// // some funky shit
|
||||||
|
// const computeLerpConst1 = (i) => {
|
||||||
|
// return Math.pow(Math.cos((1 - i) * Math.PI), 5) / 2 + 0.5
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ease in out
|
||||||
|
const computeLerpConst2 = (i) => {
|
||||||
|
return 3 * Math.pow(i, 2) - 2 * Math.pow(i, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useBuildCurves
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import Path from '@joemaddalone/path'
|
||||||
|
|
||||||
|
function randRange (min, max) {
|
||||||
|
return Math.random() * (max - min) + min
|
||||||
|
}
|
||||||
|
|
||||||
|
const useBuildTopCurvePath = (numPoints, width, height, curveHeight) => {
|
||||||
|
const curveStart = {
|
||||||
|
x: width, y: randRange(height - curveHeight, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
const curve = []
|
||||||
|
|
||||||
|
let prev = {
|
||||||
|
cx1: undefined,
|
||||||
|
cy1: undefined,
|
||||||
|
cx2: undefined,
|
||||||
|
cy2: undefined,
|
||||||
|
ex: curveStart.x,
|
||||||
|
ey: curveStart.y
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < numPoints; i++) {
|
||||||
|
const cy2 = randRange(height - curveHeight, height)
|
||||||
|
curve.push({
|
||||||
|
cx1: prev.ex - width / numPoints / 3,
|
||||||
|
cy1: prev.cy1 ? prev.ey + (prev.ey - prev.cy2) : randRange(height - curveHeight, height),
|
||||||
|
cx2: prev.ex - width / numPoints * 2 / 3,
|
||||||
|
cy2,
|
||||||
|
ex: prev.ex - width / numPoints,
|
||||||
|
ey: randRange(height - curveHeight, cy2)
|
||||||
|
})
|
||||||
|
prev = curve[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = new Path()
|
||||||
|
path.moveTo(0, 0)
|
||||||
|
path.L(width, 0)
|
||||||
|
path.L(curveStart.x, curveStart.y)
|
||||||
|
curve.forEach(curve => path.C(curve.cx1, curve.cy1, curve.cx2, curve.cy2, curve.ex, curve.ey))
|
||||||
|
path.L(0, 0)
|
||||||
|
|
||||||
|
return path.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useBuildTopCurvePath
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// useIsInViewport.js
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
const useIsInViewport = (ref, threshold = 0.5, callback = () => {}) => {
|
||||||
|
const [isInViewport, setIsInViewport] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
const newIsInViewport = entry.isIntersecting
|
||||||
|
setIsInViewport(newIsInViewport)
|
||||||
|
|
||||||
|
if (newIsInViewport) {
|
||||||
|
// debounce(callback, 50); // Call the callback when component is in view
|
||||||
|
callback() // Call the callback when component is in view
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ threshold }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (ref.current) {
|
||||||
|
observer.observe(ref.current)
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (ref.current) {
|
||||||
|
observer.unobserve(ref.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [ref, threshold, callback])
|
||||||
|
|
||||||
|
return isInViewport
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useIsInViewport
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
const useScreenSize = () => {
|
||||||
|
const [size, setSize] = useState([window.innerWidth, window.innerHeight])
|
||||||
|
|
||||||
|
const handleResize = () => setSize([window.innerWidth, window.innerHeight])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener('resize', handleResize)
|
||||||
|
|
||||||
|
return () => { window.removeEventListener('resize', handleResize) }
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useScreenSize
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
function About () {
|
||||||
|
return (
|
||||||
|
<div className='h-full'>
|
||||||
|
<h1>About Me</h1>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default About
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ChromaticText from '../Components/ChromaticText'
|
||||||
|
function Home () {
|
||||||
|
return (
|
||||||
|
<div className='flex justify-center gap-y-4 h-full'>
|
||||||
|
<div className='flex-1 grow flex flex-col justify-center gap-y-4 h-full'>
|
||||||
|
<span className=' text-7xl'>Hi, I'm</span>
|
||||||
|
<div className='text-9xl font-bold'>
|
||||||
|
<ChromaticText text={'Noah Paige'} offset='0.25rem'/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-1/2 h-full">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M15.59 14.37a6 6 0 01-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 006.16-12.12A14.98 14.98 0 009.631 8.41m5.96 5.96a14.926 14.926 0 01-5.841 2.58m-.119-8.54a6 6 0 00-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 00-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 01-2.448-2.448 14.9 14.9 0 01.06-.312m-2.24 2.39a4.493 4.493 0 00-1.757 4.306 4.493 4.493 0 004.306-1.758M16.5 9a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Home
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { motion } from 'framer-motion'
|
||||||
|
|
||||||
|
function Links () {
|
||||||
|
return (
|
||||||
|
<div className='grid grid-rows-2 grid-flow-col gap-8 place-content-around'>
|
||||||
|
<motion.a
|
||||||
|
whileHover={{ scale: 1.1 }}
|
||||||
|
whileTap={{ scale: 0.9 }}
|
||||||
|
target='_blank'
|
||||||
|
href='https://github.com/noahpaige/'
|
||||||
|
rel="noreferrer"
|
||||||
|
className='bg-purple-800 h-20 w-32 rounded-xl text-2xl font-bold flex items-center justify-center'
|
||||||
|
>
|
||||||
|
github
|
||||||
|
</motion.a>
|
||||||
|
<motion.a
|
||||||
|
whileHover={{ scale: 1.1 }}
|
||||||
|
whileTap={{ scale: 0.9 }}
|
||||||
|
target='_blank'
|
||||||
|
href='https://www.linkedin.com/in/noah-paige'
|
||||||
|
rel="noreferrer"
|
||||||
|
className='bg-blue-600 h-20 w-32 rounded-lg text-2xl font-bold flex items-center justify-center'
|
||||||
|
>
|
||||||
|
linkedin
|
||||||
|
</motion.a>
|
||||||
|
<motion.a
|
||||||
|
whileHover={{ scale: 1.1 }}
|
||||||
|
whileTap={{ scale: 0.9 }}
|
||||||
|
href='mailto:noahlandonpaige@gmail.com&subject=What's up?'
|
||||||
|
className='bg-orange-400 h-20 w-32 rounded-lg text-2xl font-bold flex items-center justify-center'
|
||||||
|
>
|
||||||
|
email me
|
||||||
|
</motion.a>
|
||||||
|
<motion.a
|
||||||
|
whileHover={{ scale: 1.1 }}
|
||||||
|
whileTap={{ scale: 0.9 }}
|
||||||
|
target='_blank'
|
||||||
|
href='https://github.com/noahpaige/'
|
||||||
|
rel="noreferrer"
|
||||||
|
className='bg-emerald-600 h-20 w-32 rounded-lg text-2xl font-bold flex items-center justify-center'
|
||||||
|
>
|
||||||
|
resume
|
||||||
|
</motion.a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Links
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ProjectCardContainer from '../Components/ProjectCardContainer'
|
||||||
|
import projectsData from '../Data/ProjectsData'
|
||||||
|
|
||||||
|
function Projects () {
|
||||||
|
return (
|
||||||
|
<div className='h-full flex flex-col justify-center'>
|
||||||
|
<div className='text-4xl'>Projects</div>
|
||||||
|
<div className='min-w-0 min-h-0 w-full xl:w-[1100px] grow relative m-auto'>
|
||||||
|
<div className='h-full w-full grid grid-cols-2 grid-rows-4 gap-4 p-4'>
|
||||||
|
{projectsData.map((pData, index) => (
|
||||||
|
<ProjectCardContainer
|
||||||
|
key={index}
|
||||||
|
title={pData.title}
|
||||||
|
classes={pData.classes}
|
||||||
|
imgSrc={pData.imgSrc}
|
||||||
|
imgPOI={pData.imgPOI}
|
||||||
|
desc={pData.desc}
|
||||||
|
content={pData.content}
|
||||||
|
tags={pData.tags}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Projects
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
function Skills () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Skills
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Skills
|
||||||
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
import App from './App'
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||||
|
(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import ReactDOM from 'react-dom/client'
|
|
||||||
import App from './App'
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<App />
|
|
||||||
</React.StrictMode>
|
|
||||||
)
|
|
||||||
Vendored
-1
@@ -1 +0,0 @@
|
|||||||
/// <reference types="vite/client" />
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
module.exports = {
|
|
||||||
mode: 'jit',
|
|
||||||
content: [
|
|
||||||
"./index.html",
|
|
||||||
"./src/**/*.{js,ts,jsx,tsx}",
|
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
require('@tailwindcss/typography'),
|
|
||||||
require("daisyui")
|
|
||||||
],
|
|
||||||
daisyui: {
|
|
||||||
styled: true,
|
|
||||||
themes: true,
|
|
||||||
base: true,
|
|
||||||
utils: true,
|
|
||||||
logs: true,
|
|
||||||
rtl: false,
|
|
||||||
darkTheme: "dark",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
module.exports = {
|
||||||
|
content: ['./src/**/*.{js,ts,jsx,tsx}'],
|
||||||
|
plugins: [require('daisyui')],
|
||||||
|
daisyui: {
|
||||||
|
themes: [
|
||||||
|
{
|
||||||
|
mytheme: {
|
||||||
|
primary: '#3ABFF8',
|
||||||
|
secondary: '#828DF8',
|
||||||
|
accent: '#F471B5',
|
||||||
|
neutral: '#1D283A',
|
||||||
|
'base-100': 'rgba(20, 15, 30)',
|
||||||
|
info: '#0CA6E9',
|
||||||
|
success: '#2BD4BD',
|
||||||
|
warning: '#F4C152',
|
||||||
|
error: '#FB6F84'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ESNext",
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
||||||
"allowJs": false,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"esModuleInterop": false,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "Node",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react-jsx"
|
|
||||||
},
|
|
||||||
"include": ["src"],
|
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "Node",
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
import tailwindcss from 'tailwindcss'
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()]
|
plugins: [react()],
|
||||||
|
commonjsOptions: {
|
||||||
|
esmExternals: true
|
||||||
|
}
|
||||||
})
|
})
|
||||||
Reference in New Issue
Block a user