Browse Source

(major) switch to vite, rewrite in typescript

Signed-off-by: Muthu Kumar <muthukumar@thefeathers.in>
pull/1/head
Muthu Kumar 3 years ago
parent
commit
ca22847f76
Signed by: mkrhere GPG Key ID: 3FD688398897097E
  1. 30
      index.html
  2. 16114
      package-lock.json
  3. 48
      package.json
  4. 12907
      pnpm-lock.yaml
  5. 39
      public/index.html
  6. 23
      src/App.js
  7. 26
      src/App.tsx
  8. 60
      src/components/Container.tsx
  9. 5
      src/components/Dashed.tsx
  10. 0
      src/index.tsx
  11. 3
      src/pages/404.tsx
  12. 60
      src/pages/Contact.tsx
  13. 18
      src/pages/Exp.tsx
  14. 5
      src/pages/Home.tsx
  15. 0
      src/pages/Live.js
  16. 15
      src/pages/Projects.tsx
  17. 14
      src/util/index.js
  18. 15
      src/util/index.ts
  19. 7
      svg.d.ts
  20. 19
      tsconfig.json
  21. 27
      vite.config.ts

30
index.html

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Personal website of MKRhere" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="192x192" href="/favicon/android-chrome-192x192.png">
<link rel="manifest" href="/favicon/site.webmanifest">
<meta name="theme-color" content="#ff5555">
<link rel="mask-icon" href="/favicon/safari-pinned-tab.svg" color="#5bbad5">
<link rel="shortcut icon" href="/favicon/favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png">
<meta name="msapplication-config" content="/favicon/browserconfig.xml">
<meta name="msapplication-TileColor" content="#ff5555">
<link rel="stylesheet" href="/fonts.css" />
<title>MKRhere</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>

16114
package-lock.json

File diff suppressed because it is too large

48
package.json

@ -2,39 +2,31 @@
"name": "pw2",
"version": "0.1.0",
"private": true,
"dependencies": {
"@reach/router": "^1.3.4",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"emotion": "^10.0.27",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.0",
"web-vitals": "^0.2.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
"start": "vite",
"build": "tsc && vite build",
"serve": "vite preview"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
"react-app"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
"dependencies": {
"emotion": "^10.0.27",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router-dom": "^5.2.0"
},
"devDependencies": {
"@svgr/rollup": "^5.5.0",
"@types/react": "^17.0.14",
"@types/react-dom": "^17.0.9",
"@types/react-router-dom": "^5.1.8",
"@vitejs/plugin-react-refresh": "^1.3.5",
"react-scripts": "4.0.0",
"typescript": "^4.3.5",
"vite": "^2.4.3",
"vite-react-jsx": "^1.1.2"
}
}

12907
pnpm-lock.yaml

File diff suppressed because it is too large

39
public/index.html

@ -1,39 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Personal website of MKRhere" />
<link rel="icon" type="image/png" sizes="16x16" href="%PUBLIC_URL%/favicon/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="192x192" href="%PUBLIC_URL%/favicon/android-chrome-192x192.png">
<link rel="manifest" href="%PUBLIC_URL%/favicon/site.webmanifest">
<meta name="theme-color" content="#ff5555">
<link rel="mask-icon" href="%PUBLIC_URL%/favicon/safari-pinned-tab.svg" color="#5bbad5">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon/favicon.ico">
<link rel="apple-touch-icon" sizes="180x180" href="%PUBLIC_URL%/favicon/apple-touch-icon.png">
<meta name="msapplication-config" content="%PUBLIC_URL%/favicon/browserconfig.xml">
<meta name="msapplication-TileColor" content="#ff5555">
<link rel="stylesheet" href="/fonts.css" />
<title>MKRhere</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

23
src/App.js

@ -1,23 +0,0 @@
import { Router } from "@reach/router";
import Home from "./pages/Home";
import Exp from "./pages/Exp";
import Projects from "./pages/Projects";
import Contact from "./pages/Contact";
import NotFound from "./pages/404";
function App() {
return (
<Router>
<Home path="/" />
<Exp path="/experience" />
<Projects path="/projects" />
<Contact path="/contact" />
<NotFound path="*" />
</Router>
);
}
export default App;

26
src/App.tsx

@ -0,0 +1,26 @@
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./pages/Home";
import Exp from "./pages/Exp";
import Projects from "./pages/Projects";
import Contact from "./pages/Contact";
import NotFound from "./pages/404";
function App() {
return (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/experience" component={Exp} />
<Route exact path="/projects" component={Projects} />
<Route exact path="/contact" component={Contact} />
<Route component={NotFound} />
</Switch>
</Router>
);
}
export default App;

60
src/components/Container.js → src/components/Container.tsx

@ -1,6 +1,6 @@
import React, { useEffect, useRef, useLayoutEffect } from "react";
import { css, cx } from "emotion";
import { Link, navigate } from "@reach/router";
import { Link, useHistory } from "react-router-dom";
import { ReactComponent as Logo } from "../assets/logo.svg";
import { ReactComponent as Right } from "../assets/arrow-right.svg";
@ -18,25 +18,37 @@ const flash = css`
const [timer, clear] = getTimeout();
const Container = ({
children,
const Container: React.FunctionComponent<{
children: (string | React.DetailedReactHTMLElement<any, HTMLElement> | React.ReactElement)[];
hideNav?: boolean;
arrowReversed?: boolean;
next?: string;
className?: string;
}> = ({
children: _children,
hideNav = false,
arrowReversed = false,
next,
className,
...props
}) => {
const logoContainer = useRef();
const logo = useRef();
const highlightCircle = useRef();
const containerChild = useRef();
const nextBtn = useRef();
children = React.Children.map(children, child => {
return React.cloneElement(child, {
style: { opacity: 0, marginBottom: "5rem", transition: "all 300ms" },
});
});
const history = useHistory();
const logoContainer = useRef<HTMLAnchorElement>(null);
const logo = useRef<SVGSVGElement>(null);
const highlightCircle = useRef<HTMLSpanElement>(null);
const containerChild = useRef<HTMLDivElement>(null);
const nextBtn = useRef<HTMLButtonElement>(null);
const children = React.Children.map(
_children,
(child: string | React.DetailedReactHTMLElement<any, HTMLElement> | React.ReactElement) =>
!child || typeof child === "string"
? child
: React.cloneElement(child, {
style: { opacity: 0, marginBottom: "5rem", transition: "all 300ms" },
}),
);
useEffect(() => {
if (highlightCircle.current) {
@ -50,7 +62,10 @@ const Container = ({
}
if (containerChild.current) {
const containerChildren = [...containerChild.current.children];
const containerChildren = [...containerChild.current.children] as (
| HTMLElement
| SVGElement
)[];
containerChildren.forEach((child, idx) => {
timer(() => {
child.style.removeProperty("opacity");
@ -69,7 +84,7 @@ const Container = ({
}, []);
const handleResize = () => {
if (logoContainer.current)
if (containerChild.current && logoContainer.current)
logoContainer.current.style.left = `${containerChild.current.getBoundingClientRect().x}px`;
};
@ -83,17 +98,17 @@ const Container = ({
// on first render
useLayoutEffect(handleResize, []);
const handleNext = e => {
const handleNext: React.MouseEventHandler<HTMLButtonElement> = e => {
if (containerChild.current) {
[...containerChild.current.children].forEach(child => {
([...containerChild.current.children] as (HTMLElement | SVGElement)[]).forEach(child => {
child.style.marginBottom = "2rem";
child.style.opacity = "0";
});
}
document.body.style.maxHeight = "100vh";
document.body.style.overflow = "hidden";
e.currentTarget.style.width = 0;
timer(() => navigate(next), 300);
e.currentTarget.style.width = "0";
timer(() => next && history.push(next), 300);
};
return (
@ -107,8 +122,8 @@ const Container = ({
`}>
{!hideNav && (
<Link
to={"/" + Math.random().toString(16).slice(2)}
ref={logoContainer}
to={"/"}
innerRef={logoContainer}
className={css`
position: absolute;
top: 8rem;
@ -141,6 +156,7 @@ const Container = ({
)}></span>
<Logo
ref={logo}
viewBox="0 0 264 264"
className={css`
position: absolute;
height: 5rem;

5
src/components/Dashed.js → src/components/Dashed.tsx

@ -1,11 +1,12 @@
import React from "react";
import { css } from "emotion";
const Dashed = props => (
const Dashed: React.FunctionComponent = ({ children }) => (
<span
className={css`
border-bottom: 1px dashed var(--text-color);
`}>
{props.children}
{children}
</span>
);

0
src/index.js → src/index.tsx

3
src/pages/404.js → src/pages/404.tsx

@ -1,4 +1,5 @@
import { Link } from "@reach/router";
import React from "react";
import { Link } from "react-router-dom";
import Container from "../components/Container";
function Home() {

60
src/pages/Contact.js → src/pages/Contact.tsx

@ -1,3 +1,4 @@
import React from "react";
import { css } from "emotion";
import { useEffect, useState } from "react";
import Container from "../components/Container";
@ -6,41 +7,48 @@ const A = css`
text-decoration: none;
`;
function Home() {
const [contact, setContact] = useState({
Twitter: { value: "MKRhere", link: "https://twitter.com/MKRhere" },
GitHub: { value: "MKRhere", link: "https://github.com/MKRhere" },
Email: {
value: "mυthυkυmαr@thεfεαthεrs.in",
link: "mailto:mυthυkυmαr@thεfεαthεrs.in",
replacer: {
υ: "u",
ε: "e",
α: "a",
},
type Contact = {
[k: string]: { value: string; link?: string; replacer?: Record<string, string> };
};
const CONTACT: Contact = {
Twitter: { value: "MKRhere", link: "https://twitter.com/MKRhere" },
GitHub: { value: "MKRhere", link: "https://github.com/MKRhere" },
Email: {
value: "mυthυkυmαr@thεfεαthεrs.in",
link: "mailto:mυthυkυmαr@thεfεαthεrs.in",
replacer: {
υ: "u",
ε: "e",
α: "a",
},
Phone: {
value: "+9Ι Γ8Δ5 Γ9 8Δ88",
link: "tel:+91Γ8Δ5Γ98Δ88",
replacer: {
Ι: "1",
Δ: "4",
Γ: "7",
},
},
Phone: {
value: "+9Ι Γ8Δ5 Γ9 8Δ88",
link: "tel:+91Γ8Δ5Γ98Δ88",
replacer: {
Ι: "1",
Δ: "4",
Γ: "7",
},
});
},
};
const Home: React.FunctionComponent = () => {
const [contact, setContact] = useState<Contact>(CONTACT);
useEffect(() => {
const deob = () => {
Object.keys(contact).forEach(key => {
const replacers = contact[key].replacer;
const sect = contact[key];
const replacers = sect.replacer;
if (replacers) {
contact[key].value = contact[key].value.replace(
sect.value = sect.value.replace(
new RegExp(Object.keys(replacers).join("|"), "g"),
r => replacers[r] || r,
);
if (contact[key].link)
contact[key].link = contact[key].link.replace(
if (sect.link)
sect.link = sect.link.replace(
new RegExp(Object.keys(replacers).join("|"), "g"),
r => replacers[r] || r,
);
@ -117,6 +125,6 @@ function Home() {
</div>
</Container>
);
}
};
export default Home;

18
src/pages/Exp.js → src/pages/Exp.tsx

@ -1,3 +1,4 @@
import React from "react";
import { css } from "emotion";
import Container from "../components/Container";
@ -20,7 +21,7 @@ const exp = [
{ title: "Feathers Studio", location: "Chennai", position: "Founder", year: "2019-present" },
];
const Circle = () => (
const Circle: React.FunctionComponent = () => (
<div>
<div
className={css`
@ -46,7 +47,14 @@ const Circle = () => (
</div>
);
const ExpUnit = ({ title, location, position, year }) => {
type Experience = {
title: string;
location: string;
position: string;
year: string;
};
const ExpUnit: React.FunctionComponent<Experience> = ({ title, location, position, year }) => {
return (
<div
className={css`
@ -73,7 +81,7 @@ const ExpUnit = ({ title, location, position, year }) => {
);
};
const getAge = date => {
const getAge = (date: string) => {
var today = new Date();
var birthDate = new Date(date);
var age = today.getFullYear() - birthDate.getFullYear();
@ -84,7 +92,7 @@ const getAge = date => {
const age = getAge("27 May 1995");
function Exp() {
const Exp: React.FunctionComponent = () => {
return (
<Container next="/projects">
<h2>Im a {age} year old developer from Chennai, India.</h2>
@ -108,6 +116,6 @@ function Exp() {
</div>
</Container>
);
}
};
export default Exp;

5
src/pages/Home.js → src/pages/Home.tsx

@ -1,7 +1,8 @@
import React from "react";
import Container from "../components/Container";
import Dashed from "../components/Dashed";
function Home() {
const Home: React.FunctionComponent = () => {
return (
<Container next="/experience">
<h1>MKRhere</h1>
@ -11,6 +12,6 @@ function Home() {
</p>
</Container>
);
}
};
export default Home;

0
src/pages/Live.js

15
src/pages/Projects.js → src/pages/Projects.tsx

@ -1,3 +1,4 @@
import React from "react";
import { css } from "emotion";
import Container from "../components/Container";
@ -46,7 +47,15 @@ const exp = [
},
];
const ProjectUnit = ({ title, url, description, cat, tags }) => {
type Project = {
title: string;
url: string;
description: string;
cat: string;
tags: string[];
};
const ProjectUnit: React.FunctionComponent<Project> = ({ title, url, description, cat, tags }) => {
return (
<div
className={css`
@ -113,7 +122,7 @@ const ProjectUnit = ({ title, url, description, cat, tags }) => {
);
};
function Exp() {
const Exp: React.FunctionComponent = () => {
return (
<Container next="/contact">
<h2>What else have I built?</h2>
@ -137,6 +146,6 @@ function Exp() {
</div>
</Container>
);
}
};
export default Exp;

14
src/util/index.js

@ -1,14 +0,0 @@
export const getTimeout = () => {
const clearables = new Set();
const timeout = (f, t) => {
const self = clearables.add(setTimeout(() => (f(), clearables.delete(self)), t));
};
const clearTimers = () => {
clearables.forEach(timer => clearTimeout(timer));
clearables.clear();
};
return [timeout, clearTimers];
};

15
src/util/index.ts

@ -0,0 +1,15 @@
export const getTimeout = () => {
const clearables = new Set<number>();
const timeout = (f: (...attr: any[]) => any, t: number) => {
const self = setTimeout(() => (f(), clearables.delete(self)), t);
clearables.add(self);
};
const clearTimers = () => {
clearables.forEach(timer => clearTimeout(timer));
clearables.clear();
};
return [timeout, clearTimers] as const;
};

7
svg.d.ts

@ -0,0 +1,7 @@
declare module "*.svg" {
import * as React from "react";
export const ReactComponent: React.FunctionComponent<
React.SVGProps<SVGSVGElement> & { title?: string; ref?: React.Ref<SVGSVGElement> | undefined }
>;
}

19
tsconfig.json

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ESNext",
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"types": ["vite/client"],
"skipLibCheck": false,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": ["./svg.d.ts", "./src"]
}

27
vite.config.ts

@ -0,0 +1,27 @@
import { defineConfig } from "vite";
import reactRefresh from "@vitejs/plugin-react-refresh";
import svgr from "@svgr/rollup";
import reactJsx from "vite-react-jsx";
// https://vitejs.dev/config/
export default defineConfig({
server: {
port: 10000,
hmr: {
port: 10000,
},
},
plugins: [
reactJsx(),
reactRefresh(),
Object.assign(
svgr({
// memo: true,
// icon: true,
ref: true,
svgo: false,
}),
{ enforce: "pre" } as const,
),
],
});
Loading…
Cancel
Save