This commit is contained in:
0
.idea/jsLibraryMappings.xml
generated
Normal file
0
.idea/jsLibraryMappings.xml
generated
Normal file
@@ -1,4 +1,4 @@
|
|||||||
import openingImage from "../img/IMG_20240725_175449_803.jpg";
|
import openingImage from "../img/DSC_5633.jpg";
|
||||||
import rainbow from "../img/IMG-0186.jpg";
|
import rainbow from "../img/IMG-0186.jpg";
|
||||||
import japanese from "../img/IMG_20230531_000224_01.jpg";
|
import japanese from "../img/IMG_20230531_000224_01.jpg";
|
||||||
import computers from "../img/IMG_3071.jpg";
|
import computers from "../img/IMG_3071.jpg";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import "./about.scss";
|
import "./about.sass";
|
||||||
import {HTMLProps, lazy} from "react";
|
import {HTMLProps, lazy} from "react";
|
||||||
import {Link} from "../router/Link";
|
import {Link} from "../router/Link";
|
||||||
import {Path} from "../router/router";
|
import {Path} from "../router/router";
|
||||||
|
|||||||
82
src/about/about.sass
Normal file
82
src/about/about.sass
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
@use "../nav/nav"
|
||||||
|
$paragraph: 20px
|
||||||
|
$max-width: 600px
|
||||||
|
$min-width: 400px
|
||||||
|
|
||||||
|
.about-page
|
||||||
|
color: white
|
||||||
|
display: flex
|
||||||
|
justify-content: center
|
||||||
|
|
||||||
|
.title
|
||||||
|
text-align: center
|
||||||
|
margin: nav.$padding
|
||||||
|
|
||||||
|
.self
|
||||||
|
width: 100%
|
||||||
|
border-bottom: white 4px solid
|
||||||
|
|
||||||
|
|
||||||
|
.about-container
|
||||||
|
min-width: $min-width
|
||||||
|
flex-shrink: 1
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
align-items: center
|
||||||
|
font-family: Neuton, serif
|
||||||
|
line-height: 40px
|
||||||
|
|
||||||
|
img:not(.self)
|
||||||
|
border: 2px white solid
|
||||||
|
|
||||||
|
p
|
||||||
|
text-indent: 30px
|
||||||
|
padding: nav.$padding
|
||||||
|
max-width: $max-width
|
||||||
|
font-size: $paragraph
|
||||||
|
|
||||||
|
h1
|
||||||
|
font-size: $paragraph * 2
|
||||||
|
padding: nav.$padding
|
||||||
|
|
||||||
|
h2
|
||||||
|
font-size: $paragraph * 1.5
|
||||||
|
padding: nav.$padding
|
||||||
|
|
||||||
|
h3
|
||||||
|
font-size: $paragraph * 1.1
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
.edge
|
||||||
|
background: #27213C
|
||||||
|
flex-grow: 1
|
||||||
|
min-width: 30%
|
||||||
|
|
||||||
|
.clothing, .computers
|
||||||
|
margin: 15px 0
|
||||||
|
display: flex
|
||||||
|
flex-direction: row
|
||||||
|
justify-content: center
|
||||||
|
|
||||||
|
|
||||||
|
.clothing>img
|
||||||
|
width: 33%
|
||||||
|
|
||||||
|
|
||||||
|
.computers
|
||||||
|
// select second
|
||||||
|
img:not(:first-child)
|
||||||
|
width: 20%
|
||||||
|
|
||||||
|
img
|
||||||
|
width: 30%
|
||||||
|
|
||||||
|
.external-link
|
||||||
|
text-decoration: underline !important
|
||||||
|
|
||||||
|
ul
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
font-size: $paragraph
|
||||||
|
max-width: $max-width
|
||||||
|
gap: 10px
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
@use "../nav/nav.scss";
|
|
||||||
$paragraph: 20px;
|
|
||||||
$max-width: 600px;
|
|
||||||
|
|
||||||
.about-page {
|
|
||||||
color: white;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
text-align: center;
|
|
||||||
margin: nav.$padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
.self {
|
|
||||||
width: 100%;
|
|
||||||
border-bottom: white 4px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.about-container {
|
|
||||||
margin-top: 2 * nav.$padding;
|
|
||||||
flex-shrink: 1;
|
|
||||||
max-width: 75%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
font-family: Neuton;
|
|
||||||
line-height: 40px;
|
|
||||||
|
|
||||||
img:not(.self) {
|
|
||||||
border: 2px white solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
text-indent: 30px;
|
|
||||||
margin: 10px;
|
|
||||||
max-width: $max-width;
|
|
||||||
font-size: $paragraph;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: $paragraph * 2;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: $paragraph * 1.5;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: $paragraph * 1.1;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.edge {
|
|
||||||
background:#27213C;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clothing, .computers {
|
|
||||||
margin: 15px 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clothing>img {
|
|
||||||
width: 33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.computers {
|
|
||||||
// select second
|
|
||||||
img:not(:first-child) {
|
|
||||||
width: 20%;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.external-link {
|
|
||||||
text-decoration: underline !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
font-size: $paragraph;
|
|
||||||
max-width: $max-width;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 13 MiB After Width: | Height: | Size: 13 MiB |
|
Before Width: | Height: | Size: 5.6 MiB After Width: | Height: | Size: 5.6 MiB |
1
src/img/blog-box.svg
Normal file
1
src/img/blog-box.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="#282c34" fill-opacity="1" d="M0,192L30,186.7C60,181,120,171,180,149.3C240,128,300,96,360,106.7C420,117,480,171,540,186.7C600,203,660,181,720,154.7C780,128,840,96,900,122.7C960,149,1020,235,1080,245.3C1140,256,1200,192,1260,186.7C1320,181,1380,235,1410,261.3L1440,288L1440,0L1410,0C1380,0,1320,0,1260,0C1200,0,1140,0,1080,0C1020,0,960,0,900,0C840,0,780,0,720,0C660,0,600,0,540,0C480,0,420,0,360,0C300,0,240,0,180,0C120,0,60,0,30,0L0,0Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 524 B |
27
src/index.sass
Normal file
27
src/index.sass
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$phone: 750px
|
||||||
|
|
||||||
|
html, body
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
font-family: Inter, sans-serif
|
||||||
|
-webkit-font-smoothing: antialiased
|
||||||
|
-moz-osx-font-smoothing: grayscale
|
||||||
|
|
||||||
|
#root
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
min-height: 100vh
|
||||||
|
background: black
|
||||||
|
color: white
|
||||||
|
|
||||||
|
// disable image dragging
|
||||||
|
img
|
||||||
|
user-select: none
|
||||||
|
pointer-events: none
|
||||||
|
|
||||||
|
button
|
||||||
|
background: none
|
||||||
|
border: none
|
||||||
|
padding: 0
|
||||||
|
color: white
|
||||||
|
cursor: pointer
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: black;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
||||||
sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable image dragging
|
|
||||||
img {
|
|
||||||
user-select: none;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
|
||||||
monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import './index.scss';
|
import './index.sass';
|
||||||
import {RouterProvider} from "./router/RouterContext";
|
import {RouterProvider} from "./router/RouterContext";
|
||||||
import "@fontsource/inter"; // Defaults to weight 400
|
import "@fontsource/inter"; // Defaults to weight 400
|
||||||
import "@fontsource/neuton";
|
import "@fontsource/neuton";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import "./canvas.sass";
|
||||||
import {motion} from "framer-motion";
|
import {motion} from "framer-motion";
|
||||||
import "./canvas.scss";
|
|
||||||
import {Canvas as ThreeCanvas, useFrame} from "@react-three/fiber";
|
import {Canvas as ThreeCanvas, useFrame} from "@react-three/fiber";
|
||||||
import {Vector3} from "three";
|
import {Vector3} from "three";
|
||||||
import {Moon} from "./Moon";
|
import {Moon} from "./Moon";
|
||||||
@@ -9,7 +9,8 @@ export default function Canvas() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="canvas">
|
<main className="canvas">
|
||||||
<ThreeCanvas gl={{antialias: true, alpha: true}} >
|
<section className="canvas-ctr">
|
||||||
|
<ThreeCanvas shadows>
|
||||||
<Sphere/>
|
<Sphere/>
|
||||||
<Moon radius={8} color="pink" step={0.01}/>
|
<Moon radius={8} color="pink" step={0.01}/>
|
||||||
<Moon radius={6} color="grey" step={0.02}/>
|
<Moon radius={6} color="grey" step={0.02}/>
|
||||||
@@ -17,9 +18,10 @@ export default function Canvas() {
|
|||||||
<Stars/>
|
<Stars/>
|
||||||
<directionalLight color="#E3A857" args={[1,10]} position={[100,10,100]} />
|
<directionalLight color="#E3A857" args={[1,10]} position={[100,10,100]} />
|
||||||
</ThreeCanvas>
|
</ThreeCanvas>
|
||||||
<motion.h2 className="welcome" animate={{ rotate: 360 }}>
|
</section>
|
||||||
|
<motion.h1 className="welcome" animate={{ rotate: 360 }}>
|
||||||
Welcome to my blog
|
Welcome to my blog
|
||||||
</motion.h2>
|
</motion.h1>
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/index/canvas.sass
Normal file
18
src/index/canvas.sass
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
@use "../index"
|
||||||
|
|
||||||
|
.canvas
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
|
||||||
|
.canvas-ctr
|
||||||
|
width: 100%
|
||||||
|
height: 100vh
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
flex-grow: 1
|
||||||
|
|
||||||
|
.welcome
|
||||||
|
position: absolute
|
||||||
|
font-family: Neuton, serif
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
.canvas {
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
background: black;
|
|
||||||
color: white;
|
|
||||||
z-index: -999;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.welcome {
|
|
||||||
text-align: center;
|
|
||||||
position: absolute;
|
|
||||||
top: calc(50% + 50px);
|
|
||||||
font-family: Neuton;
|
|
||||||
font-size: 30px;
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
import {Link} from "../router/Link";
|
import {Link} from "../router/Link";
|
||||||
import {motion} from "framer-motion";
|
|
||||||
import {ROUTES} from "../router/Route";
|
import {ROUTES} from "../router/Route";
|
||||||
import ResumePdf from "../img/resume.pdf";
|
import ResumePdf from "../img/resume.pdf";
|
||||||
|
import {motion} from "motion/react";
|
||||||
|
|
||||||
export function Links() {
|
export function Links() {
|
||||||
|
|
||||||
return <div className="links">
|
return <div className="links">
|
||||||
<Spacers length={ROUTES.length + 1} />
|
|
||||||
<Link href="/" className="name">
|
<Link href="/" className="name">
|
||||||
<motion.h1 className="name">Tymur Arsentiev</motion.h1>
|
<motion.h1>Tymur Arsentiev</motion.h1>
|
||||||
</Link>
|
</Link>
|
||||||
{
|
{
|
||||||
ROUTES.map(([path, name]) =>
|
ROUTES.map(([path, name]) =>
|
||||||
@@ -22,12 +21,3 @@ export function Links() {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
function Spacers(props: {length: number}) {
|
|
||||||
const spacers = [];
|
|
||||||
for (let i = 0; i < props.length; i++) {
|
|
||||||
spacers.push(<div key={i}/>);
|
|
||||||
}
|
|
||||||
|
|
||||||
return spacers;
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import "./menu.scss";
|
import "./menu.sass";
|
||||||
import {motion, animate} from "framer-motion";
|
import {animate, motion} from "framer-motion";
|
||||||
import {useEffect} from "react";
|
import {useEffect} from "react";
|
||||||
|
|
||||||
const articles = [
|
const articles = [
|
||||||
|
|||||||
@@ -1,34 +1,32 @@
|
|||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||||
import {faBars} from "@fortawesome/free-solid-svg-icons/faBars";
|
import {faBars} from "@fortawesome/free-solid-svg-icons/faBars";
|
||||||
import {Menu} from "./Menu";
|
|
||||||
import {ReactComponent as Wave} from "../img/menu-wave.svg";
|
import {ReactComponent as Wave} from "../img/menu-wave.svg";
|
||||||
import "./nav.scss";
|
import "./nav.sass";
|
||||||
import {Links} from "./Links";
|
import {Links} from "./Links";
|
||||||
|
import {Menu} from "./Menu";
|
||||||
|
|
||||||
export default function Nav() {
|
export default function Nav() {
|
||||||
const [active, setActive] = useState(false);
|
const [active, setActive] = useState(false);
|
||||||
|
|
||||||
return <>
|
return <div className="nav-ctr">
|
||||||
<nav>
|
<nav>
|
||||||
<MenuButton active={active} setActive={setActive}/>
|
<MenuButton active={active} setActive={setActive}/>
|
||||||
<Links/>
|
<Links/>
|
||||||
</nav>
|
</nav>
|
||||||
<Menu active={active}/>
|
|
||||||
<Wave className="wave" />
|
<Wave className="wave" />
|
||||||
</>;
|
<Menu active={active}/>
|
||||||
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function MenuButton(props : { active: boolean, setActive: (_:boolean) => void }) {
|
function MenuButton(props : { active: boolean, setActive: (_:boolean) => void }) {
|
||||||
const {active, setActive} = props;
|
const {active, setActive} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="menu-btn">
|
<button className="menu-btn link">
|
||||||
<button>
|
|
||||||
<FontAwesomeIcon icon={faBars} onClick={function() {
|
<FontAwesomeIcon icon={faBars} onClick={function() {
|
||||||
setActive(!active);
|
setActive(!active);
|
||||||
}}/>
|
}}/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/nav/menu.sass
Normal file
13
src/nav/menu.sass
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
@use "nav"
|
||||||
|
|
||||||
|
.menu
|
||||||
|
position: absolute
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
background: nav.$menu
|
||||||
|
color: white
|
||||||
|
width: 25%
|
||||||
|
height: 100vh
|
||||||
|
|
||||||
|
div
|
||||||
|
padding: nav.$padding
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
@use "./nav";
|
|
||||||
|
|
||||||
.menu {
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background: nav.$menu;
|
|
||||||
z-index: 10;
|
|
||||||
color: white;
|
|
||||||
width: 25%;
|
|
||||||
height: 100vh;
|
|
||||||
|
|
||||||
div {
|
|
||||||
padding: nav.$padding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
50
src/nav/nav.sass
Normal file
50
src/nav/nav.sass
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
@use "../index"
|
||||||
|
$padding: 15px
|
||||||
|
$menu: #282c34
|
||||||
|
|
||||||
|
.menu-btn
|
||||||
|
align-self: center
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
align-items: center
|
||||||
|
font-size: 30px
|
||||||
|
|
||||||
|
|
||||||
|
// hide menu button and center links
|
||||||
|
@media screen and (max-width: index.$phone)
|
||||||
|
.menu-btn
|
||||||
|
display: none
|
||||||
|
position: absolute
|
||||||
|
|
||||||
|
.links
|
||||||
|
justify-content: center
|
||||||
|
|
||||||
|
nav
|
||||||
|
background-color: $menu
|
||||||
|
padding: $padding
|
||||||
|
display: flex
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
|
||||||
|
.name
|
||||||
|
margin: auto
|
||||||
|
flex-grow: 1
|
||||||
|
h1
|
||||||
|
margin: auto 0
|
||||||
|
font-size: 40px
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.links
|
||||||
|
flex-grow: 1
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
display: flex
|
||||||
|
flex-wrap: wrap
|
||||||
|
gap: 15px
|
||||||
|
|
||||||
|
|
||||||
|
.wave
|
||||||
|
user-select: none
|
||||||
|
pointer-events: none
|
||||||
|
position: absolute
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
$padding: 15px;
|
|
||||||
$menu: #282c34;
|
|
||||||
|
|
||||||
.menu-btn>button:hover {
|
|
||||||
opacity: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-btn>button:active {
|
|
||||||
opacity: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-btn {
|
|
||||||
align-self: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
button {
|
|
||||||
font-size: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
background-color: $menu;
|
|
||||||
padding: $padding;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav>a{
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name>h1 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
margin: auto;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wave {
|
|
||||||
user-select: none;
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.links {
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 1;
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav {
|
|
||||||
font-family: Inter;
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import "./link.scss";
|
import "./link.sass";
|
||||||
|
|
||||||
import React, {HTMLProps, ReactNode} from "react";
|
import React, {HTMLProps, ReactNode} from "react";
|
||||||
import {useRouter} from "./RouterContext";
|
import {useRouter} from "./RouterContext";
|
||||||
@@ -7,7 +7,7 @@ import {Path} from "./router";
|
|||||||
export function Link(props: {href: Path, children : ReactNode, target?: '_blank' } & HTMLProps<HTMLAnchorElement>) {
|
export function Link(props: {href: Path, children : ReactNode, target?: '_blank' } & HTMLProps<HTMLAnchorElement>) {
|
||||||
let {children, className, href} = props;
|
let {children, className, href} = props;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
className = `${className} link`;
|
className = `${className ?? ""} link`.trim();
|
||||||
|
|
||||||
function onClick(event: React.MouseEvent) {
|
function onClick(event: React.MouseEvent) {
|
||||||
// perform the default action if _blank
|
// perform the default action if _blank
|
||||||
|
|||||||
@@ -1,31 +1,40 @@
|
|||||||
import React, {PropsWithChildren} from "react";
|
import React, {PropsWithChildren} from "react";
|
||||||
import {useRouter} from "./RouterContext";
|
import {useRouter} from "./RouterContext";
|
||||||
import {Path} from "./router";
|
import {Path} from "./router";
|
||||||
|
import {ArticlesList} from "../articles/ArticlesList";
|
||||||
|
|
||||||
const Canvas = React.lazy(() => import("../index/Canvas"));
|
type Route = [Path, string];
|
||||||
const About = React.lazy(() => import("../about/AboutPage"));
|
type Routes = [Route, Route, Route];
|
||||||
|
export const ROUTES: Routes = [
|
||||||
export const ROUTES: [Path, string][] = [
|
|
||||||
["/", "Home"],
|
["/", "Home"],
|
||||||
["/about", "About"],
|
["/about", "About"],
|
||||||
["/articles", "Articles"]
|
["/articles", "Articles"]
|
||||||
];
|
];
|
||||||
|
|
||||||
function Route(props: PropsWithChildren & {path: Path}) {
|
function Route(props: PropsWithChildren & {route: Route}) {
|
||||||
const {path, children} = props;
|
const {route: [path], children} = props;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return <>{router.current === path && children}</>
|
return <>{router.current === path && children}</>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Canvas = React.lazy(() => import("../index/Canvas"));
|
||||||
|
const About = React.lazy(() => import("../about/AboutPage"));
|
||||||
|
|
||||||
|
|
||||||
export function Routes() {
|
export function Routes() {
|
||||||
|
const [index, about, articles] = ROUTES;
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<Route path={ROUTES[0]![0]}>
|
<Route route={index}>
|
||||||
<Canvas/>
|
<Canvas/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={ROUTES[1]![0]}>
|
<Route route={about}>
|
||||||
<About/>
|
<About/>
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route route={articles}>
|
||||||
|
<ArticlesList/>
|
||||||
|
</Route>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
@@ -22,11 +22,11 @@ export function RouterProvider(props : PropsWithChildren) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <RouterContext.Provider value={{
|
return <RouterContext.Provider value={{
|
||||||
pushPage: function(newPath: Path) {
|
pushPage(newPath: Path) {
|
||||||
window.history.pushState(newPath,"", newPath);
|
window.history.pushState(newPath,"", newPath);
|
||||||
setPath(newPath as Path);
|
setPath(newPath as Path);
|
||||||
},
|
},
|
||||||
replacePage: function(newPath: Path) {
|
replacePage(newPath: Path) {
|
||||||
window.history.replaceState(newPath, "", newPath);
|
window.history.replaceState(newPath, "", newPath);
|
||||||
setPath(newPath as Path);
|
setPath(newPath as Path);
|
||||||
},
|
},
|
||||||
|
|||||||
8
src/router/link.sass
Normal file
8
src/router/link.sass
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.link
|
||||||
|
color: white
|
||||||
|
text-decoration: none
|
||||||
|
text-align: center
|
||||||
|
&:hover
|
||||||
|
opacity: 90%
|
||||||
|
&:active
|
||||||
|
opacity: 80%
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
.link {
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link:hover {
|
|
||||||
opacity: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link:active {
|
|
||||||
opacity: 80%;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user