This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import {createContext, lazy, LazyExoticComponent, PropsWithChildren, useContext, useState} from "react";
|
||||
import {Path} from "../router/router";
|
||||
import {MDXContent} from "mdx/types";
|
||||
import {lazy, LazyExoticComponent} from "react";
|
||||
import allBlack from "../img/IMG_3095.jpg";
|
||||
import {Reader} from "./Reader";
|
||||
|
||||
export interface Article {
|
||||
name: string,
|
||||
@@ -21,4 +22,19 @@ export const ARTICLES: Article[] = [
|
||||
published: new Date(Date.parse("3/15/2026")),
|
||||
article: lazy(() => import("./mdx/high-school.mdx"))
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
const articleContext =
|
||||
createContext<ReturnType<typeof useState<Article | undefined>>>([undefined, function(){}]);
|
||||
|
||||
export function useArticle() {
|
||||
return useContext(articleContext);
|
||||
}
|
||||
|
||||
export default function ArticleProvider({ children }: PropsWithChildren) {
|
||||
const state = useState<Article | undefined>(undefined);
|
||||
return <articleContext.Provider value={state}>
|
||||
<Reader/>
|
||||
{children}
|
||||
</articleContext.Provider>;
|
||||
}
|
||||
@@ -1,31 +1,28 @@
|
||||
import {Article, ARTICLES} from "./articles";
|
||||
import {useState} from "react";
|
||||
import {Article, ARTICLES, useArticle} from "./ArticleContext";
|
||||
import "./list.sass";
|
||||
import {ReactComponent as BoxSvg} from "../img/blog-box.svg";
|
||||
import {Reader} from "./Reader";
|
||||
|
||||
|
||||
export function ArticlesList() {
|
||||
const [reading, setReading] = useState<Article | null>(null);
|
||||
|
||||
return <main className="art-ctr">
|
||||
<section className="spacer"/>
|
||||
<section className="articles">
|
||||
{
|
||||
ARTICLES.map(a =>
|
||||
<BlogBox key={a.name} article={a} setReading={setReading}/>
|
||||
<BlogBox key={a.name} article={a}/>
|
||||
)
|
||||
}
|
||||
</section>
|
||||
<section className="spacer"/>
|
||||
<Reader post={reading} setPost={setReading} />
|
||||
<Reader/>
|
||||
</main>
|
||||
}
|
||||
|
||||
function BlogBox(props: { article: Article, setReading: (_: Article) => void}) {
|
||||
const {article,
|
||||
article: {name, description, thumbnail, published},
|
||||
setReading} = props;
|
||||
function BlogBox(props: { article: Article }) {
|
||||
const {article, article: {name, description, thumbnail, published} } = props;
|
||||
|
||||
const [, setReading] = useArticle();
|
||||
|
||||
return (
|
||||
<button className="link" onClick={() => setReading(article)}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Article} from "./articles";
|
||||
import {useArticle} from "./ArticleContext";
|
||||
import {motion} from "framer-motion";
|
||||
import {useAnimate} from "motion/react";
|
||||
import {useAnimate, animate as animateClass} from "motion/react";
|
||||
import {TargetAndTransition} from "motion";
|
||||
import {useEffect} from "react";
|
||||
|
||||
@@ -9,22 +9,40 @@ const READER : Record<Transition, TargetAndTransition> = {
|
||||
closed : { opacity: 0 },
|
||||
open : { opacity: 1 }
|
||||
};
|
||||
const ARTICLE : Record<Transition, TargetAndTransition> = {
|
||||
closed : { top: "-100%" },
|
||||
open : { top: "50%" }
|
||||
};
|
||||
|
||||
type Props = { post: Article | null, setPost: (_: Article | null) => void };
|
||||
export function Reader({ post, setPost }: Props) {
|
||||
const [ scope, animate ]= useAnimate<HTMLElement>();
|
||||
|
||||
export function Reader() {
|
||||
const [ scope, animate ] = useAnimate<HTMLElement>();
|
||||
const [post, setPost] = useArticle();
|
||||
|
||||
useEffect(() => {
|
||||
if(post && scope.current) {
|
||||
animate(scope.current, READER.open);
|
||||
animateClass(".read-box", ARTICLE.open);
|
||||
}
|
||||
}, [post, scope]);
|
||||
|
||||
useEffect(() => {
|
||||
function pressEscape(e: KeyboardEvent) {
|
||||
e.key === "Escape" && handleClose();
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", pressEscape);
|
||||
return () => document.removeEventListener("keydown", pressEscape);
|
||||
}, []);
|
||||
|
||||
function handleClose() {
|
||||
animate(scope.current, READER.closed)
|
||||
.then(() => setPost(null));
|
||||
Promise.all([
|
||||
animate(scope.current, READER.closed),
|
||||
animateClass(".read-box", ARTICLE.closed)
|
||||
]).then(() => setPost(undefined));
|
||||
}
|
||||
|
||||
|
||||
return post ? (
|
||||
<motion.article
|
||||
key="reader"
|
||||
@@ -33,9 +51,9 @@ export function Reader({ post, setPost }: Props) {
|
||||
animate={READER.open}
|
||||
onClick={handleClose}
|
||||
className="reader link">
|
||||
<section onClick={e => e.stopPropagation()}>
|
||||
<motion.section className="read-box" initial={{top: "-100%"}} animate={{top: "50%"}} onClick={e => e.stopPropagation()}>
|
||||
<post.article/>
|
||||
</section>
|
||||
</motion.section>
|
||||
</motion.article>
|
||||
) : <></>;
|
||||
}
|
||||
@@ -54,17 +54,21 @@ $entry-width: 400px
|
||||
flex-wrap: wrap
|
||||
|
||||
.reader
|
||||
font-family: Neuton, serif
|
||||
border: 2px solid black
|
||||
&, > section
|
||||
position: absolute
|
||||
position: fixed
|
||||
top: 50%
|
||||
left: 50%
|
||||
transform: translate(-50%, -50%)
|
||||
|
||||
z-index: 9999
|
||||
width: 100vw
|
||||
height: 100vh
|
||||
min-height: 100vh
|
||||
background: rgba(0,0,0,0.5)
|
||||
> section
|
||||
margin-top: nav.$padding
|
||||
background: white
|
||||
color: black
|
||||
width: 75vw
|
||||
height: 75vh
|
||||
width: 50vw
|
||||
min-height: 75vh
|
||||
@@ -1 +1,9 @@
|
||||
# My high school experience
|
||||
import allBlack from "../../img/IMG_3095.jpg";
|
||||
import "./high-school.sass";
|
||||
|
||||
|
||||
# My high school experience
|
||||
|
||||
<img src={allBlack} width={500} alt="turtleneck thumbnail"/>
|
||||
|
||||
My coding journey started with Code Combat, a Python coding RPG game
|
||||
3
src/articles/mdx/high-school.sass
Normal file
3
src/articles/mdx/high-school.sass
Normal file
@@ -0,0 +1,3 @@
|
||||
article
|
||||
img
|
||||
width: 500px
|
||||
@@ -5,6 +5,7 @@ import {RouterProvider} from "./router/RouterContext";
|
||||
import "@fontsource/inter"; // Defaults to weight 400
|
||||
import "@fontsource/neuton";
|
||||
import {Routes} from "./router/Route";
|
||||
import ArticleProvider from "./articles/ArticleContext";
|
||||
|
||||
const Nav = React.lazy(() => import("./nav/Nav"));
|
||||
|
||||
@@ -15,8 +16,10 @@ const root = ReactDOM.createRoot(
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<RouterProvider>
|
||||
<Nav/>
|
||||
<Routes/>
|
||||
<ArticleProvider>
|
||||
<Nav/>
|
||||
<Routes/>
|
||||
</ArticleProvider>
|
||||
</RouterProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import "./menu.sass";
|
||||
import {animate, motion} from "framer-motion";
|
||||
import {useEffect} from "react";
|
||||
import {ARTICLES, useArticle} from "../articles/ArticleContext";
|
||||
|
||||
|
||||
const articles = [
|
||||
"Test 123",
|
||||
"Hello",
|
||||
"Title of Album"
|
||||
];
|
||||
const hidden = -999;
|
||||
|
||||
export function Menu({active} : {active: boolean}) {
|
||||
const [, setReading] = useArticle();
|
||||
useEffect(() => {
|
||||
if(active) {
|
||||
animate(".menu", {left : 0})
|
||||
@@ -20,11 +18,18 @@ export function Menu({active} : {active: boolean}) {
|
||||
|
||||
return (
|
||||
<motion.section initial={{left: hidden}} className="menu">
|
||||
{
|
||||
articles.map(title => <div key={title}>
|
||||
{title}
|
||||
</div>)
|
||||
}
|
||||
<ul>
|
||||
{
|
||||
ARTICLES.map((article) =>
|
||||
<li key={article.name}>
|
||||
<button className="link" onClick={() => setReading(article)} >
|
||||
{article.name}
|
||||
</button>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
|
||||
</motion.section>
|
||||
)
|
||||
}
|
||||
@@ -2,8 +2,15 @@
|
||||
|
||||
.menu
|
||||
position: absolute
|
||||
display: flex
|
||||
flex-direction: column
|
||||
z-index: 999
|
||||
&, ul
|
||||
display: flex
|
||||
flex-direction: column
|
||||
list-style: none
|
||||
button
|
||||
font-weight: bold
|
||||
text-decoration: underline
|
||||
font-size: 1rem
|
||||
background: nav.$menu
|
||||
color: white
|
||||
width: 25%
|
||||
|
||||
Reference in New Issue
Block a user