Changed all functional components to class components. added better comments, minor optimizations here and there, removed some unfinished posts from blogs

This commit is contained in:
Kim, Jimin 2021-05-20 15:56:41 +09:00
parent c850184bc7
commit 046dd05713
20 changed files with 987 additions and 927 deletions

View file

@ -22,15 +22,8 @@
"sourceType": "module" "sourceType": "module"
}, },
"plugins": ["react", "@typescript-eslint"], "plugins": ["react", "@typescript-eslint"],
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": ["off"]
}
}
],
"rules": { "rules": {
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/explicit-module-boundary-types": "off",
"react/jsx-uses-vars": "error", "react/jsx-uses-vars": "error",
"react/no-unknown-property": [ "react/no-unknown-property": [

View file

@ -1,8 +0,0 @@
---
title: death of miracle
author: developomp
date: May 15, 2021
---
Understanding things what we once called magic.
Science.

View file

@ -1,25 +0,0 @@
---
title: guide to arch linux
author: developomp
date: May 13, 2021
---
# Guide to Arch Linux
## Why arch linux?
## Getting started
### Philosophy
### Arch wiki
### Command line
## Installation
## display manager
## window manager
# Conclusion

View file

@ -1,6 +0,0 @@
학교로 갔다
집으로 왔다
일을 한다
죽었다
나는 죽었다
나는 죽었다

View file

@ -1,12 +1,12 @@
import React, { createContext } from "react"
import { BrowserRouter as Router, Switch, Route } from "react-router-dom" import { BrowserRouter as Router, Switch, Route } from "react-router-dom"
import { ThemeProvider, createGlobalStyle } from "styled-components" import { ThemeProvider, createGlobalStyle } from "styled-components"
import { HelmetProvider } from "react-helmet-async" import { HelmetProvider } from "react-helmet-async"
import storage from "local-storage-fallback" import storage from "local-storage-fallback"
import { useState, useEffect } from "react"
import Spinner from "./components/Spinner"
import LanguageContext from "./LanguageContext"
import theming from "./theming" import theming from "./theming"
import Spinner from "./components/Spinner"
import Navbar from "./components/Navbar" import Navbar from "./components/Navbar"
import Footer from "./components/Footer" import Footer from "./components/Footer"
@ -126,91 +126,98 @@ blockquote {
} }
` `
function App() { interface AppProps {}
/**
* Loading
*/
const [isLoading, setLoading] = useState(true)
interface AppState {
isLoading: boolean
currentTheme: string
currentLanguage: string
}
export const LanguageContext = createContext({
language: "",
// eslint-disable-next-line @typescript-eslint/no-empty-function
toggleLanguage: () => {},
})
export default class App extends React.Component<AppProps, AppState> {
constructor(props) {
super(props)
this.state = {
isLoading: true,
currentTheme: storage.getItem("theme") || "dark", // get theme from storage and set to "dark" mode if not set already
currentLanguage: storage.getItem("lang") || "en", // get language from storage and set to "en" if not set already
}
}
componentDidMount() {
// show loading screen until all fonts are loaded. // show loading screen until all fonts are loaded.
// Experimental feature. Not fully supported on all browsers (IE, I'm looking at you). // Experimental feature. Not fully supported on all browsers (IE, I'm looking at you).
// https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet // https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet
useEffect(() => {
// checks if document.fonts.onloadingdone is supported on the browser // checks if document.fonts.onloadingdone is supported on the browser
if (typeof document.fonts.onloadingdone != undefined) { if (typeof document.fonts.onloadingdone != undefined) {
document.fonts.onloadingdone = () => { document.fonts.onloadingdone = () => {
setLoading(false) this.setState({ isLoading: false })
} }
} else { } else {
setLoading(false) this.setState({ isLoading: false })
}
} }
}, [])
/**
* Theme
*/
const [currentTheme, _setTheme] = useState(
storage.getItem("theme") || "dark" // get theme from storage and set to "dark" mode if not set already
)
componentDidUpdate(_, prevState) {
if (this.state.currentTheme !== prevState.currentTheme) {
// save theme when it is changed // save theme when it is changed
useEffect(() => { storage.setItem("theme", this.state.currentTheme)
storage.setItem("theme", currentTheme)
}, [currentTheme])
/**
* Language
*/
const [currentLanguage, _setLanguage] = useState(
storage.getItem("lang") || "en" // get language from storage and set to "en" if not set already
)
// save language when it is changed
useEffect(() => {
storage.setItem("lang", currentLanguage)
}, [currentLanguage])
const languageState = {
language: currentLanguage,
toggleLanguage: () => {
// cycle through languages
let setLanguageTo = "en"
if (currentLanguage == "en") setLanguageTo = "kr"
_setLanguage(setLanguageTo)
},
} }
if (this.state.currentLanguage !== prevState.currentLanguage) {
// save language when it is changed
storage.setItem("lang", this.state.currentLanguage)
}
}
render() {
return ( return (
<HelmetProvider> <HelmetProvider>
<ThemeProvider <ThemeProvider
theme={{ theme={{
currentTheme: currentTheme, currentTheme: this.state.currentTheme,
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
setTheme: (setThemeTo) => _setTheme(setThemeTo), // make setTheme function available in other components setTheme: (setThemeTo) =>
this.setState({ currentTheme: setThemeTo }), // make setTheme function available in other components
}}
>
<LanguageContext.Provider
value={{
language: this.state.currentLanguage,
toggleLanguage: () => {
// cycle through languages
let setLanguageTo = "en"
if (this.state.currentLanguage == "en")
setLanguageTo = "kr"
this.setState({
currentLanguage: setLanguageTo,
})
},
}} }}
> >
<LanguageContext.Provider value={languageState}>
<GlobalStyle /> <GlobalStyle />
<Router> <Router>
<Navbar /> <Navbar />
<div id="content"> <div id="content">
{isLoading ? ( {this.state.isLoading ? (
<Spinner <Spinner size={200} />
size={200}
color={
currentTheme == "dark"
? theming.dark.color1
: theming.light.color1
}
/>
) : ( ) : (
<Switch> <Switch>
<Route <Route
exact exact
path="/" path="/"
component={() => ( component={() => (
<Home howMany={4} title="Home" /> <Home
howMany={4}
title="Home"
/>
)} )}
/> />
<Route <Route
@ -245,5 +252,4 @@ function App() {
</HelmetProvider> </HelmetProvider>
) )
} }
}
export default App

View file

@ -1,11 +0,0 @@
/**
* go to App.tsx and search for `languageState` to see the actual values
*/
import { createContext } from "react"
export default createContext({
language: "",
// eslint-disable-next-line @typescript-eslint/no-empty-function
toggleLanguage: () => {},
})

View file

@ -1,9 +1,11 @@
import React from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faGithub } from "@fortawesome/free-brands-svg-icons" import { faGithub } from "@fortawesome/free-brands-svg-icons"
import styled from "styled-components" import styled from "styled-components"
import theming from "../theming" import theming from "../theming"
const StyledFooter = styled.footer` export default class Footer extends React.Component {
StyledFooter = styled.footer`
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin-bottom: 1px; /* footer goes outside the page by 1 px for some reason */ margin-bottom: 1px; /* footer goes outside the page by 1 px for some reason */
@ -29,7 +31,7 @@ const StyledFooter = styled.footer`
} }
` `
const StyledLink = styled.a` StyledLink = styled.a`
width: 30px; width: 30px;
font-size: 2rem; font-size: 2rem;
color: ${(props) => color: ${(props) =>
@ -47,23 +49,27 @@ const StyledLink = styled.a`
} }
` `
function Footer() { StyledStrong = styled.strong`
font-size: 1.1rem;
`
render() {
return ( return (
<StyledFooter> <this.StyledFooter>
<div className="logo"> <div className="logo">
Copyright &copy; develo<strong>p</strong>omp Copyright &copy; develo
<this.StyledStrong>p</this.StyledStrong>omp
</div> </div>
<div className="icons"> <div className="icons">
<StyledLink <this.StyledLink
href="https://github.com/developomp/developomp-site" href="https://github.com/developomp/developomp-site"
target="_blank" target="_blank"
> >
<FontAwesomeIcon icon={faGithub} /> <FontAwesomeIcon icon={faGithub} />
</StyledLink> </this.StyledLink>
</div> </div>
</StyledFooter> </this.StyledFooter>
) )
} }
}
export default Footer

View file

@ -1,12 +1,18 @@
import React from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faLanguage } from "@fortawesome/free-solid-svg-icons" import { faLanguage } from "@fortawesome/free-solid-svg-icons"
import styled from "styled-components" import styled from "styled-components"
import ReactTooltip from "react-tooltip" import ReactTooltip from "react-tooltip"
import LanguageContext from "../LanguageContext" import { LanguageContext } from "../App"
import theming from "../theming" import theming from "../theming"
const StyledThemeButton = styled.div<{ language: string }>` interface StyledThemeButtonProps {
language: string
}
export default class LanguageToggleButton extends React.Component {
StyledThemeButton = styled.div<StyledThemeButtonProps>`
${theming.styles.navbarButtonStyle} ${theming.styles.navbarButtonStyle}
${(props) => ${(props) =>
props.language == "en" props.language == "en"
@ -17,32 +23,33 @@ const StyledThemeButton = styled.div<{ language: string }>`
-ms-transform: scaleX(-1);"}; -ms-transform: scaleX(-1);"};
` `
function LanguageToggleButton() { languageName = (language) => {
function languageName(language) {
let name = "English" let name = "English"
if (language == "kr") name = "Korean" if (language == "kr") name = "Korean"
return name return name
} }
render() {
return ( return (
<LanguageContext.Consumer> <LanguageContext.Consumer>
{({ language, toggleLanguage }) => ( {({ language, toggleLanguage }) => (
<> <>
<StyledThemeButton <this.StyledThemeButton
data-tip data-tip
data-for="language" data-for="language"
onClick={toggleLanguage} onClick={toggleLanguage}
language={language} language={language}
> >
<FontAwesomeIcon icon={faLanguage} /> <FontAwesomeIcon icon={faLanguage} />
</StyledThemeButton> </this.StyledThemeButton>
<ReactTooltip id="language" type="dark" effect="solid"> <ReactTooltip id="language" type="dark" effect="solid">
<span>Using {languageName(language)} language</span> <span>
Using {this.languageName(language)} language
</span>
</ReactTooltip> </ReactTooltip>
</> </>
)} )}
</LanguageContext.Consumer> </LanguageContext.Consumer>
) )
} }
}
export default LanguageToggleButton

View file

@ -1,3 +1,4 @@
import React from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faGithub } from "@fortawesome/free-brands-svg-icons" import { faGithub } from "@fortawesome/free-brands-svg-icons"
import styled from "styled-components" import styled from "styled-components"
@ -11,7 +12,8 @@ import Sidebar from "./Sidebar"
import ThemeToggleButton from "./ThemeToggleButton" import ThemeToggleButton from "./ThemeToggleButton"
import LanguageToggleButton from "./LanguageToggleButton" import LanguageToggleButton from "./LanguageToggleButton"
const StyledNav = styled.nav` export default class Navbar extends React.Component {
StyledNav = styled.nav`
display: flex; display: flex;
align-items: center; align-items: center;
height: 2rem; height: 2rem;
@ -34,62 +36,60 @@ const StyledNav = styled.nav`
} }
` `
const StyledNavLinks = styled.div` StyledNavLinks = styled.div`
@media only screen and (max-width: ${theming.size.screen_size1}) { @media only screen and (max-width: ${theming.size.screen_size1}) {
display: none; display: none;
} }
` `
const StyledImg = styled.img` StyledImg = styled.img`
height: 2rem; height: 2rem;
margin: 1rem; margin: 1rem;
` `
const StyledLink = styled(Link)` StyledLink = styled(Link)`
${theming.styles.navbarButtonStyle} ${theming.styles.navbarButtonStyle}
` `
const StyledALink = styled.a` StyledALink = styled.a`
${theming.styles.navbarButtonStyle} ${theming.styles.navbarButtonStyle}
` `
render() {
function Navbar() {
return ( return (
<StyledNav> <this.StyledNav>
<Link to="/"> <Link to="/">
<StyledImg <this.StyledImg
src={process.env.PUBLIC_URL + "/icon/icon_circle.svg"} src={process.env.PUBLIC_URL + "/icon/icon_circle.svg"}
/> />
</Link> </Link>
<StyledNavLinks> <this.StyledNavLinks>
{NavbarData.map((item, index) => { {NavbarData.map((item, index) => {
return ( return (
<StyledLink key={index} to={item.path}> <this.StyledLink key={index} to={item.path}>
{item.title} {item.title}
</StyledLink> </this.StyledLink>
) )
})} })}
</StyledNavLinks> </this.StyledNavLinks>
<ThemeToggleButton /> <ThemeToggleButton />
<LanguageToggleButton /> <LanguageToggleButton />
<StyledALink <this.StyledALink
data-tip data-tip
data-for="github" data-for="github"
href="https://github.com/developomp/developomp-site" href="https://github.com/developomp/developomp-site"
target="_blank" target="_blank"
> >
<FontAwesomeIcon icon={faGithub} /> <FontAwesomeIcon icon={faGithub} />
</StyledALink> </this.StyledALink>
<ReactTooltip id="github" type="dark" effect="solid"> <ReactTooltip id="github" type="dark" effect="solid">
<span>View source code</span> <span>View source code</span>
</ReactTooltip> </ReactTooltip>
<SearchBox /> <SearchBox />
<Sidebar /> <Sidebar />
</StyledNav> </this.StyledNav>
) )
} }
}
export default Navbar

View file

@ -1,16 +1,19 @@
import React from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faSearch } from "@fortawesome/free-solid-svg-icons" import { faSearch } from "@fortawesome/free-solid-svg-icons"
import styled from "styled-components" import styled from "styled-components"
import theming from "../theming" import theming from "../theming"
const StyledSearchBoxContainer = styled.div` export default class Navbar extends React.Component {
StyledSearchBoxContainer = styled.div`
display: flex; display: flex;
justify-content: left; justify-content: left;
margin: 0.5rem;
align-items: center; align-items: center;
background-color: ${(props) => background-color: ${(props) =>
theming.theme(props.theme.currentTheme, { theming.theme(props.theme.currentTheme, {
light: "white", light: "white",
dark: "#202225", dark: "#2F3136",
})}; })};
color: ${(props) => color: ${(props) =>
theming.theme(props.theme.currentTheme, { theming.theme(props.theme.currentTheme, {
@ -27,7 +30,7 @@ const StyledSearchBoxContainer = styled.div`
} }
` `
const StyledSearchBox = styled.input` StyledSearchBox = styled.input`
width: 80%; width: 80%;
border: none; border: none;
border-right: 1rem; border-right: 1rem;
@ -38,17 +41,20 @@ const StyledSearchBox = styled.input`
color: inherit; color: inherit;
` `
const StyledSearchButton = styled(FontAwesomeIcon)` StyledSearchButton = styled(FontAwesomeIcon)`
cursor: pointer; cursor: pointer;
` `
function Navbar() { render() {
return ( return (
<StyledSearchBoxContainer> <this.StyledSearchBoxContainer>
<StyledSearchBox type="text" name="search" placeholder="Search" /> <this.StyledSearchBox
<StyledSearchButton icon={faSearch} /> type="text"
</StyledSearchBoxContainer> name="search"
placeholder="Search"
/>
<this.StyledSearchButton icon={faSearch} />
</this.StyledSearchBoxContainer>
) )
} }
}
export default Navbar

View file

@ -1,6 +1,6 @@
import { useState } from "react" import React from "react"
import styled, { css } from "styled-components" import styled, { css } from "styled-components"
import NavbarData from "../data/NavbarData" import NavbarData, { Item } from "../data/NavbarData"
import ReactTooltip from "react-tooltip" import ReactTooltip from "react-tooltip"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
@ -9,28 +9,46 @@ import { faEllipsisV, faTimes } from "@fortawesome/free-solid-svg-icons"
import theming from "../theming" import theming from "../theming"
import SubMenu from "./SubMenu" import SubMenu from "./SubMenu"
interface StateProps { interface SidebarProps {}
interface SidebarState {
isSidebarOpen: boolean isSidebarOpen: boolean
} }
const CommonSidebarToggleButtonStyle = css` export default class Sidebar extends React.Component<
SidebarProps,
SidebarState
> {
constructor(props) {
super(props)
this.state = {
isSidebarOpen: false,
}
}
// for some reason this.setState only works if this is an arrow function
toggleSidebar = () => {
this.setState({ isSidebarOpen: !this.state.isSidebarOpen })
document.body.style.overflow = this.state.isSidebarOpen
? "scroll"
: "hidden"
}
CommonSidebarToggleButtonStyle = css`
${theming.styles.navbarButtonStyle} ${theming.styles.navbarButtonStyle}
font-size: "1.5rem";
width: 1.5rem; width: 1.5rem;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
margin: 0.1rem;
@media only screen and (min-width: ${theming.size.screen_size1}) { @media only screen and (min-width: ${theming.size.screen_size1}) {
display: none; display: none;
} }
` `
const StyledToggleSidebarButton = styled.div` StyledToggleSidebarButton = styled.div`
${CommonSidebarToggleButtonStyle} ${this.CommonSidebarToggleButtonStyle}
` `
const StyledToggleSidebarButton2 = styled.div` StyledToggleSidebarButton2 = styled.div`
${CommonSidebarToggleButtonStyle} ${this.CommonSidebarToggleButtonStyle}
border-radius: 0; border-radius: 0;
margin: auto; margin: auto;
width: 90%; width: 90%;
@ -38,7 +56,7 @@ const StyledToggleSidebarButton2 = styled.div`
font-size: 1.1rem; font-size: 1.1rem;
` `
const StyledOverlay = styled.div<StateProps>` StyledOverlay = styled.div<SidebarState>`
display: ${(props) => (props.isSidebarOpen ? "block" : "none")}; display: ${(props) => (props.isSidebarOpen ? "block" : "none")};
position: fixed; position: fixed;
top: 0; top: 0;
@ -54,7 +72,7 @@ const StyledOverlay = styled.div<StateProps>`
} }
` `
const SidebarNav = styled.nav<StateProps>` SidebarNav = styled.nav<SidebarState>`
width: 250px; width: 250px;
height: 100vh; height: 100vh;
display: flex; display: flex;
@ -78,48 +96,43 @@ const SidebarNav = styled.nav<StateProps>`
})}; })};
` `
const SidebarWrap = styled.div` SidebarWrap = styled.div`
width: 100%; width: 100%;
` `
const Sidebar = () => { render() {
const [isSidebarOpen, setSidebar] = useState(false)
function toggleSidebar() {
setSidebar(!isSidebarOpen)
document.body.style.overflow = isSidebarOpen ? "scroll" : "hidden"
}
return ( return (
<> <>
<StyledOverlay <this.StyledOverlay
isSidebarOpen={isSidebarOpen} isSidebarOpen={this.state.isSidebarOpen}
onClick={toggleSidebar} onClick={this.toggleSidebar}
/> />
<StyledToggleSidebarButton <this.StyledToggleSidebarButton
data-tip data-tip
data-for="sidebar" data-for="sidebar"
onClick={toggleSidebar} onClick={this.toggleSidebar}
> >
<FontAwesomeIcon icon={faEllipsisV}></FontAwesomeIcon> <FontAwesomeIcon icon={faEllipsisV}></FontAwesomeIcon>
<ReactTooltip id="sidebar" type="dark" effect="solid"> <ReactTooltip id="sidebar" type="dark" effect="solid">
<span>open sidebar</span> <span>open sidebar</span>
</ReactTooltip> </ReactTooltip>
</StyledToggleSidebarButton> </this.StyledToggleSidebarButton>
<SidebarNav isSidebarOpen={isSidebarOpen}> <this.SidebarNav isSidebarOpen={this.state.isSidebarOpen}>
<SidebarWrap> <this.SidebarWrap>
<StyledToggleSidebarButton2 onClick={toggleSidebar}> <this.StyledToggleSidebarButton2
<FontAwesomeIcon icon={faTimes}></FontAwesomeIcon> Close onClick={this.toggleSidebar}
</StyledToggleSidebarButton2> >
{NavbarData.map((item, index) => { <FontAwesomeIcon icon={faTimes}></FontAwesomeIcon>{" "}
Close
</this.StyledToggleSidebarButton2>
{NavbarData.map((item: Item, index) => {
return <SubMenu item={item} key={index} /> return <SubMenu item={item} key={index} />
})} })}
</SidebarWrap> </this.SidebarWrap>
</SidebarNav> </this.SidebarNav>
</> </>
) )
} }
}
export default Sidebar

View file

@ -2,56 +2,58 @@
* inspired by https://github.com/dmitrymorozoff/react-spinners-kit/tree/master/src/components/whisper * inspired by https://github.com/dmitrymorozoff/react-spinners-kit/tree/master/src/components/whisper
*/ */
import React from "react"
import styled, { keyframes } from "styled-components" import styled, { keyframes } from "styled-components"
const motion = keyframes` interface BallInterface {
0% { size: number
key: string
index: number
}
interface WrapperInterface {
size: number
}
interface SpinnerProps {
size: number
}
export default class Spinner extends React.Component<SpinnerProps> {
motion = keyframes`
from {
transform: scale(1, 1); transform: scale(1, 1);
opacity: 1; opacity: 1;
} }
100% { to {
transform: scale(0, 0); transform: scale(0, 0);
opacity: 0; opacity: 0;
} }
` `
const spin = keyframes` spin = keyframes`
0% { from {
transform: rotate(0deg); transform: rotate(0deg);
} }
25% { to {
transform: rotate(90deg);
}
50% {
transform: rotate(180deg);
}
75% {
transform: rotate(270deg);
}
100% {
transform: rotate(360deg); transform: rotate(360deg);
} }
` `
interface BallInterface { Ball = styled.div<BallInterface>`
color: string
size: number
key: string
index: number
}
const Ball = styled.div<BallInterface>`
float: left; float: left;
clear: right; clear: right;
margin: ${(props) => props.size / 15}px; margin: ${(props) => props.size / 15}px;
width: ${(props) => props.size / 5}px; width: ${(props) => props.size / 5}px;
height: ${(props) => props.size / 5}px; height: ${(props) => props.size / 5}px;
border-radius: 2px; border-radius: 2px;
background-color: ${(props) => props.color}; background-color: lightgrey;
animation-name: ${motion}; animation-name: ${this.motion};
animation-direction: alternate; animation-direction: alternate;
animation-duration: 800ms; animation-duration: 800ms;
animation-timing-function: linear;
animation-iteration-count: infinite; animation-iteration-count: infinite;
/* use for loops here? */
&:nth-child(1) { &:nth-child(1) {
animation-delay: 200ms; animation-delay: 200ms;
} }
@ -81,27 +83,25 @@ const Ball = styled.div<BallInterface>`
} }
` `
interface WrapperInterface { Wrapper = styled.div<WrapperInterface>`
size: number
}
const Wrapper = styled.div<WrapperInterface>`
margin: 5rem auto 0 auto; margin: 5rem auto 0 auto;
width: 200px; width: 200px;
height: 200px; height: 200px;
animation: ${spin} 10s infinite; animation-timing-function: linear;
animation: ${this.spin} 10s infinite;
` `
function Spinner({ size, color }) { balls: unknown[] = []
const balls: unknown[] = [] constructor(props) {
super(props)
let keyValue = 0 let keyValue = 0
const countBallsInLine = 3 const countBallsInLine = 3
for (let i = 0; i < countBallsInLine; i++) { for (let i = 0; i < countBallsInLine; i++) {
for (let j = 0; j < countBallsInLine; j++) { for (let j = 0; j < countBallsInLine; j++) {
balls.push( this.balls.push(
<Ball <this.Ball
color={color} size={this.props.size}
size={size}
key={keyValue.toString()} key={keyValue.toString()}
index={keyValue} index={keyValue}
/> />
@ -109,8 +109,9 @@ function Spinner({ size, color }) {
keyValue++ keyValue++
} }
} }
return <Wrapper size={size}>{balls}</Wrapper>
} }
export default Spinner render() {
return <this.Wrapper size={this.props.size}>{this.balls}</this.Wrapper>
}
}

View file

@ -1,9 +1,22 @@
import { useState } from "react" import React from "react"
import { Link } from "react-router-dom" import { Link } from "react-router-dom"
import styled from "styled-components" import styled from "styled-components"
import theming from "../theming" import theming from "../theming"
import { Item } from "../data/NavbarData"
const SidebarLink = styled(Link)` interface SubMenuProps {
item: Item
}
interface SubMenuState {
isSubNavOpen: boolean
}
export default class SubMenu extends React.Component<
SubMenuProps,
SubMenuState
> {
SidebarLink = styled(Link)`
${theming.styles.navbarButtonStyle}; ${theming.styles.navbarButtonStyle};
display: flex; display: flex;
width: 100%; width: 100%;
@ -16,11 +29,11 @@ const SidebarLink = styled(Link)`
list-style: none; list-style: none;
` `
const SidebarLabel = styled.span` SidebarLabel = styled.span`
margin-left: 16px; margin-left: 16px;
` `
const DropdownLink = styled(Link)` DropdownLink = styled(Link)`
background: #414757; background: #414757;
height: 60px; height: 60px;
padding-left: 3rem; padding-left: 3rem;
@ -36,39 +49,52 @@ const DropdownLink = styled(Link)`
} }
` `
function SubMenu({ item }) { constructor(props) {
const [isSubNavOpen, setSubNav] = useState(false) super(props)
this.state = {
isSubNavOpen: false,
}
}
const showSubNav = () => setSubNav(!isSubNavOpen) showSubNav = () => this.setState({ isSubNavOpen: !this.state.isSubNavOpen })
render() {
return ( return (
<> <>
<SidebarLink to={item.path} onClick={item.subNav && showSubNav}> <this.SidebarLink
to={this.props.item.path}
onClick={this.props.item.subNav && this.showSubNav}
>
<div> <div>
{item.icon} {this.props.item.icon}
<SidebarLabel>{item.title}</SidebarLabel> <this.SidebarLabel>
{this.props.item.title}
</this.SidebarLabel>
</div> </div>
<div> <div>
{item.subNav && isSubNavOpen {this.props.item.subNav && this.state.isSubNavOpen
? item.iconOpened ? this.props.item.iconOpened
: item.subNav : this.props.item.subNav
? item.iconClosed ? this.props.item.iconClosed
: null} : null}
</div> </div>
</SidebarLink> </this.SidebarLink>
{/* not used as of the moment */} {/* not used as of the moment */}
{isSubNavOpen && {this.state.isSubNavOpen && // check if subNav is open
item.subNav.map((item, index) => { this.props.item.subNav && // check if subNav exists in that item
this.props.item.subNav.map((item, index) => {
// shows all items in subNav
return ( return (
<DropdownLink to={item.path} key={index}> <this.DropdownLink to={item.path} key={index}>
{item.icon} {item.icon}
<SidebarLabel>{item.title}</SidebarLabel> <this.SidebarLabel>
</DropdownLink> {item.title}
</this.SidebarLabel>
</this.DropdownLink>
) )
})} })}
</> </>
) )
} }
}
export default SubMenu

View file

@ -1,3 +1,4 @@
import React from "react"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faMoon, faSun } from "@fortawesome/free-solid-svg-icons" import { faMoon, faSun } from "@fortawesome/free-solid-svg-icons"
import styled, { ThemeConsumer } from "styled-components" import styled, { ThemeConsumer } from "styled-components"
@ -6,7 +7,8 @@ import ReactTooltip from "react-tooltip"
import theming from "../theming" import theming from "../theming"
const StyledThemeButton = styled.div` export default class Navbar extends React.Component {
StyledThemeButton = styled.div`
${theming.styles.navbarButtonStyle} ${theming.styles.navbarButtonStyle}
${(props) => ${(props) =>
theming.theme(props.theme.currentTheme, { theming.theme(props.theme.currentTheme, {
@ -17,18 +19,19 @@ const StyledThemeButton = styled.div`
-ms-transform: scaleX(-1);", -ms-transform: scaleX(-1);",
})}; })};
` `
render() {
function Navbar() {
return ( return (
<ThemeConsumer> <ThemeConsumer>
{({ currentTheme, setTheme }) => ( {({ currentTheme, setTheme }) => (
<> <>
<StyledThemeButton <this.StyledThemeButton
data-tip data-tip
data-for="theme" data-for="theme"
className="right" className="right"
onClick={() => onClick={() =>
setTheme(currentTheme === "dark" ? "light" : "dark") setTheme(
currentTheme === "dark" ? "light" : "dark"
)
} }
> >
{currentTheme == "dark" && ( {currentTheme == "dark" && (
@ -37,7 +40,7 @@ function Navbar() {
{currentTheme == "light" && ( {currentTheme == "light" && (
<FontAwesomeIcon icon={faSun} /> <FontAwesomeIcon icon={faSun} />
)} )}
</StyledThemeButton> </this.StyledThemeButton>
<ReactTooltip id="theme" type="dark" effect="solid"> <ReactTooltip id="theme" type="dark" effect="solid">
<span>Using {currentTheme} theme</span> <span>Using {currentTheme} theme</span>
</ReactTooltip> </ReactTooltip>
@ -46,5 +49,4 @@ function Navbar() {
</ThemeConsumer> </ThemeConsumer>
) )
} }
}
export default Navbar

View file

@ -8,7 +8,17 @@ import {
faHiking, faHiking,
} from "@fortawesome/free-solid-svg-icons" } from "@fortawesome/free-solid-svg-icons"
export default [ // item from sidebar data
export type Item = {
path: string
subNav?: Array<Item>
icon: JSX.Element
title: string
iconOpened?: JSX.Element
iconClosed?: JSX.Element
}
const NavbarData: Array<Item> = [
{ {
title: "Home", title: "Home",
path: "/", path: "/",
@ -40,3 +50,5 @@ export default [
icon: <FontAwesomeIcon icon={faUserTie} />, icon: <FontAwesomeIcon icon={faUserTie} />,
}, },
] ]
export default NavbarData

View file

@ -1,3 +1,4 @@
import React from "react"
import { Link } from "react-router-dom" import { Link } from "react-router-dom"
import styled from "styled-components" import styled from "styled-components"
import theming from "../theming" import theming from "../theming"
@ -6,7 +7,16 @@ import { Helmet } from "react-helmet-async"
import pages from "../pages.json" import pages from "../pages.json"
const StyledPostList = styled.div` interface HomeProps {
title?: string
howMany?: number
}
export default class Home extends React.Component<HomeProps, { lol: boolean }> {
h1Text: string
PostCards: Array<unknown> = []
StyledPostList = styled.div`
padding-top: 2rem; padding-top: 2rem;
margin: auto; margin: auto;
text-align: center; text-align: center;
@ -17,18 +27,18 @@ const StyledPostList = styled.div`
})}; })};
` `
const StyledH1 = styled.h1` StyledH1 = styled.h1`
margin-bottom: 20px; margin-bottom: 20px;
font-weight: 500; font-weight: 500;
margin: 0; margin: 0;
` `
const StyledTitle = styled.h1` StyledTitle = styled.h1`
font-size: 2rem; font-size: 2rem;
font-style: bold; font-style: bold;
` `
const StyledLink = styled(Link)` StyledLink = styled(Link)`
text-decoration: none; text-decoration: none;
color: ${(props) => color: ${(props) =>
@ -42,22 +52,19 @@ const StyledLink = styled(Link)`
} }
` `
const StyledPostCard = styled.div` StyledPostCard = styled.div`
box-shadow: 0 4px 10px rgb(0 0 0 / 10%); box-shadow: 0 4px 10px rgb(0 0 0 / 10%);
text-align: left; text-align: left;
margin-bottom: 20px; margin-bottom: 20px;
padding: 10px 20px; padding: 10px 20px;
` `
function Home(props) { constructor(props) {
const PostCards: Array<unknown> = [] super(props)
let howMany = props.howMany let howMany = props.howMany
const isLimited = Boolean(howMany) const isLimited = Boolean(howMany)
let h1Text = "All posts" this.h1Text = isLimited ? "All posts" : `${howMany} recent posts`
if (isLimited) {
h1Text = `${howMany} recent posts`
}
for (const pagePath in pages) { for (const pagePath in pages) {
if (isLimited && howMany <= 0) continue if (isLimited && howMany <= 0) continue
@ -65,13 +72,20 @@ function Home(props) {
const post = pages[pagePath] const post = pages[pagePath]
post.title = post.meta?.title ? post.meta.title : "Unknown title" post.title = post.meta?.title ? post.meta.title : "Unknown title"
post.date = post.meta?.date ? post.meta.date : "Unknown date" post.date = post.meta?.date ? post.meta.date : "Unknown date"
post.author = post.meta?.author ? post.meta.author : "Unknown author" post.author = post.meta?.author
? post.meta.author
: "Unknown author"
PostCards.push( this.PostCards.push(
<StyledPostCard key={pagePath} className="card main-content"> <this.StyledPostCard
<StyledTitle> key={pagePath}
<StyledLink to={pagePath}>{post.title}</StyledLink> className="card main-content"
</StyledTitle> >
<this.StyledTitle>
<this.StyledLink to={pagePath}>
{post.title}
</this.StyledLink>
</this.StyledTitle>
<small> <small>
Published on {post.date} by {post.author} Published on {post.date} by {post.author}
</small> </small>
@ -86,19 +100,23 @@ function Home(props) {
}} }}
></div> ></div>
<small> <small>
<StyledLink to={pagePath}>Read more</StyledLink> <this.StyledLink to={pagePath}>
Read more
</this.StyledLink>
</small> </small>
</StyledPostCard> </this.StyledPostCard>
) )
howMany-- howMany--
} }
}
render() {
return ( return (
<> <>
<Helmet> <Helmet>
<title>pomp | {props.title}</title> <title>pomp | {this.props.title}</title>
<meta property="og:title" content={props.title} /> <meta property="og:title" content={this.props.title} />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="http://developomp.com" /> <meta property="og:url" content="http://developomp.com" />
<meta <meta
@ -108,13 +126,12 @@ function Home(props) {
<meta property="og:description" content="" /> <meta property="og:description" content="" />
</Helmet> </Helmet>
<StyledPostList> <this.StyledPostList>
<StyledH1>{h1Text}</StyledH1> <this.StyledH1>{this.h1Text}</this.StyledH1>
<br /> <br />
{PostCards} {this.PostCards}
</StyledPostList> </this.StyledPostList>
</> </>
) )
} }
}
export default Home

View file

@ -1,8 +1,10 @@
import React from "react"
import styled from "styled-components" import styled from "styled-components"
import theming from "../theming" import theming from "../theming"
import { Helmet } from "react-helmet-async" import { Helmet } from "react-helmet-async"
const StyledNotFound = styled.div` export default class NotFound extends React.Component {
StyledNotFound = styled.div`
margin: auto; margin: auto;
margin-top: 2rem; margin-top: 2rem;
text-align: center; text-align: center;
@ -13,11 +15,11 @@ const StyledNotFound = styled.div`
})}; })};
` `
const Styled404 = styled.h1` Styled404 = styled.h1`
font-size: 3rem; font-size: 3rem;
` `
function NotFound() { render() {
return ( return (
<> <>
<Helmet> <Helmet>
@ -30,14 +32,16 @@ function NotFound() {
property="og:image" property="og:image"
content="http://developomp.com/icon/icon.svg" content="http://developomp.com/icon/icon.svg"
/> />
<meta property="og:description" content="Page does not exist" /> <meta
property="og:description"
content="Page does not exist"
/>
</Helmet> </Helmet>
<StyledNotFound className="card main-content"> <this.StyledNotFound className="card main-content">
<Styled404>404</Styled404> <this.Styled404>404</this.Styled404>
the page you are looking for does not exist. :( the page you are looking for does not exist. :(
</StyledNotFound> </this.StyledNotFound>
</> </>
) )
} }
}
export default NotFound

View file

@ -1,44 +1,38 @@
import React from "react"
import marked from "marked" import marked from "marked"
import NotFound from "./notfound" import NotFound from "./notfound"
import { Helmet } from "react-helmet-async" import { Helmet } from "react-helmet-async"
import pages from "../pages.json" import pages from "../pages.json"
import { useParams } from "react-router-dom"
function Page() { export default class Page extends React.Component {
const path = `/${useParams().path}` // eslint-disable-next-line @typescript-eslint/no-explicit-any
const fetched = pages[path] fetched: any
if (!fetched) return <NotFound />
constructor(props) {
super(props)
const fetched = pages[props.location.pathname]
if (!fetched) return
// to prevent wrapping. I don't want to touch prettier stuff
const idk = "Unknown"
fetched.content = fetched?.content ? fetched.content : "No content" fetched.content = fetched?.content ? fetched.content : "No content"
fetched.toc = fetched.meta?.toc ? fetched.meta.toc : undefined fetched.toc = fetched.meta?.toc ? fetched.meta.toc : undefined
fetched.title = fetched.meta?.title ? fetched.meta.title : "No title" fetched.title = fetched.meta?.title ? fetched.meta.title : "No title"
fetched.date = fetched.meta?.date ? fetched.meta.date : `${idk} date` fetched.date = fetched.meta?.date ? fetched.meta.date : "Unknown date"
fetched.author = fetched.meta?.author fetched.author = fetched.meta?.author
? fetched.meta.author ? fetched.meta.author
: `${idk} author` : "Unknown author"
const TableOfContents = fetched.toc && ( this.fetched = fetched
<> }
<div className="card">
<strong>Table of Content:</strong> render() {
<div if (!this.fetched) return <NotFound />
className="link-color"
dangerouslySetInnerHTML={{
__html: marked(fetched.toc),
}}
></div>
</div>
<hr />
</>
) // add toc if it exists
return ( return (
<> <>
<Helmet> <Helmet>
<title>pomp | {fetched.title}</title> <title>pomp | {this.fetched.title}</title>
<meta property="og:title" content="Page Not Found" /> <meta property="og:title" content="Page Not Found" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
@ -47,25 +41,43 @@ function Page() {
property="og:image" property="og:image"
content="http://developomp.com/icon/icon.svg" content="http://developomp.com/icon/icon.svg"
/> />
<meta property="og:description" content="Page does not exist" /> <meta
property="og:description"
content="Page does not exist"
/>
</Helmet> </Helmet>
<div className="card main-content"> <div className="card main-content">
<h2>{fetched.title}</h2> <h2>{this.fetched.title}</h2>
<small> <small>
Published on {fetched.date} by {fetched.author} Published on {this.fetched.date} by{" "}
{this.fetched.author}
</small> </small>
<hr /> <hr />
{TableOfContents} {
this.fetched.toc && (
<>
<div className="card">
<strong>Table of Content:</strong>
<div <div
className="link-color" className="link-color"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: marked(fetched.content), __html: marked(this.fetched.toc),
}}
></div>
</div>
<hr />
</>
) // add toc if it exists
}
<div
className="link-color"
dangerouslySetInnerHTML={{
__html: marked(this.fetched.content),
}} }}
></div> ></div>
</div> </div>
</> </>
) )
} }
}
export default Page

View file

@ -1,19 +1,25 @@
import React from "react"
import styled from "styled-components" import styled from "styled-components"
import { Helmet } from "react-helmet-async" import { Helmet } from "react-helmet-async"
const StyledPortfolio = styled.div`` export default class Portfolio extends React.Component {
StyledPortfolio = styled.div``
const StyledH1 = styled.h1` StyledH1 = styled.h1`
font-size: 3rem; font-size: 3rem;
` `
function Portfolio() { render() {
return ( return (
<> <>
<Helmet> <Helmet>
<title>pomp | Portfolio</title> <title>pomp | Portfolio</title>
<meta property="og:title" content="developomp's Portfolio" /> <meta
property="og:title"
content="developomp's Portfolio"
/>
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="http://developomp.com" /> <meta property="og:url" content="http://developomp.com" />
<meta <meta
@ -25,12 +31,11 @@ function Portfolio() {
content="developomp's Portfolio" content="developomp's Portfolio"
/> />
</Helmet> </Helmet>
<StyledPortfolio className="card main-content"> <this.StyledPortfolio className="card main-content">
<StyledH1>Portfolio</StyledH1> <this.StyledH1>Portfolio</this.StyledH1>
My projects My projects
</StyledPortfolio> </this.StyledPortfolio>
</> </>
) )
} }
}
export default Portfolio