feat: typescript!

This commit is contained in:
gitlimes
2024-06-09 14:41:57 +02:00
parent 6520817a62
commit b831218a41
31 changed files with 341 additions and 120 deletions

View File

@@ -1,12 +0,0 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": ["./src/components/*"],
"@/utils/*": ["./src/utils/*"],
"@/public/*": ["./public/*"],
"@/locales/*": ["./locales/*"],
"@/app/*": ["./src/app/*"]
}
}
}

5
next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -1,4 +1,8 @@
/** @type {import('next').NextConfig} */
// @ts-check
/**
* @type {import('next').NextConfig}
**/
const nextConfig = {
reactStrictMode: true,
@@ -8,28 +12,28 @@ const nextConfig = {
protocol: 'https',
hostname: 'github.com',
port: '',
pathname: '/**'
pathname: '/**',
},
{
protocol: 'https',
hostname: 'avatars.githubusercontent.com',
port: '',
pathname: '/u/**'
pathname: '/u/**',
},
{
protocol: 'https',
hostname: 'github.githubassets.com',
port: '',
pathname: '/images/**'
pathname: '/images/**',
},
{
protocol: 'https',
hostname: 'cdn.discordapp.com',
port: '',
pathname: '/avatars/**'
}
]
}
pathname: '/avatars/**',
},
],
},
};
export default nextConfig;

59
package-lock.json generated
View File

@@ -32,6 +32,9 @@
"sharp": "^0.32.5"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/lodash.merge": "^4.6.9",
"@types/react": "18.3.3",
"eslint-config-next": "^13.4.19"
}
},
@@ -1580,6 +1583,16 @@
"@types/ms": "*"
}
},
"node_modules/@types/fs-extra": {
"version": "11.0.4",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz",
"integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==",
"dev": true,
"dependencies": {
"@types/jsonfile": "*",
"@types/node": "*"
}
},
"node_modules/@types/http-cache-semantics": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
@@ -1591,6 +1604,15 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
"node_modules/@types/jsonfile": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz",
"integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/keyv": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
@@ -1599,6 +1621,21 @@
"@types/node": "*"
}
},
"node_modules/@types/lodash": {
"version": "4.17.5",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz",
"integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==",
"dev": true
},
"node_modules/@types/lodash.merge": {
"version": "4.6.9",
"resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz",
"integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==",
"dev": true,
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/ms": {
"version": "0.7.34",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
@@ -1612,11 +1649,27 @@
"undici-types": "~5.26.4"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.12",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
"dev": true
},
"node_modules/@types/qs": {
"version": "6.9.15",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz",
"integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg=="
},
"node_modules/@types/react": {
"version": "18.3.3",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
"integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/responselike": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
@@ -2437,6 +2490,12 @@
"node": ">= 8"
}
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",

View File

@@ -34,6 +34,9 @@
"sharp": "^0.32.5"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/lodash.merge": "^4.6.9",
"@types/react": "18.3.3",
"eslint-config-next": "^13.4.19"
},
"browserslist": [

View File

@@ -11,7 +11,7 @@ const poppins = Poppins({
subsets: ['latin'], // this is the preloaded subset, all subsets will be available
});
export default function RootLayout({ children }) {
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html className={`${poppins.variable}`}>
<body style={{ paddingTop: '48px' }}>
@@ -19,7 +19,7 @@ export default function RootLayout({ children }) {
{children}
<Footer />
<Script
strategy="lazyOnLoad"
strategy="lazyOnload"
src="https://static.cloudflareinsights.com/beacon.min.js"
data-cf-beacon='{"token": "ea6695987d664a7f90874c9c0dee9385"}'
/>
@@ -60,7 +60,6 @@ export const metadata = {
card: 'summary_large_image',
site: '@PretendoNetwork',
creator: '@PretendoNetwork',
url: 'https://pretendo.network/',
images: ['https://pretendo.network/assets/images/opengraph/opengraph-image.png'],
},
robots: {

View File

@@ -11,7 +11,7 @@ import ShowcaseSection from '@/components/ShowcaseSection/ShowcaseSection';
import { getLocale } from '@/utils/locale';
import ImageFix from '@/components/ImageFix/ImageFix';
import Image, { StaticImageData } from 'next/image';
import styles from './page.module.css';
@@ -20,14 +20,26 @@ import networkImage from '@/public/assets/images/showcase/network.png';
import pcmouseImage from '@/public/assets/images/showcase/pcmouse.png';
import wiiuchatImage from '@/public/assets/images/showcase/wiiuchat.png';
const showcaseImages = {
interface ShowcaseImages {
[key: string]: StaticImageData;
}
const showcaseImages: ShowcaseImages = {
juxt: juxtImage,
network: networkImage,
pcmouse: pcmouseImage,
wiiuchat: wiiuchatImage,
};
export default function Home(ctx) {
interface TeamMemberObject {
special: boolean;
name: string;
caption: string;
picture: string;
github: string;
}
export default function Home() {
const { locale } = getLocale('TODO');
return (
@@ -42,7 +54,7 @@ export default function Home(ctx) {
<Caption>Our project has many components. Here are some of them.</Caption>
</Section>
{locale.showcase.map((el, i) => {
{locale.showcase.map((el: { title: string; caption: string; image: string }, i: number) => {
return (
<ShowcaseSection
title={el.title}
@@ -55,7 +67,7 @@ export default function Home(ctx) {
})}
<Section className={styles.showcaseTail} contentClassName={styles.content}>
<ImageFix
<Image
src={wiiuchatImage}
className={styles.image}
alt=""
@@ -89,7 +101,7 @@ export default function Home(ctx) {
<Caption center>{locale.credits.text}</Caption>
<div className={styles.teamWrapper}>
{locale.credits.people.map((el, i) => {
{locale.credits.people.map((el: TeamMemberObject, i: number) => {
return (
<TeamCard name={el.name} caption={el.caption} pic={el.picture} link={el.github} key={i} />
);
@@ -108,7 +120,7 @@ export default function Home(ctx) {
let ar = locale.specialThanks.people;
ar = ar.slice(0, Math.round(ar.length / 2));
ar = ar.concat(ar).concat(ar);
return ar.map((el, i) => {
return ar.map((el: TeamMemberObject, i: number) => {
return (
<TeamCard
compact
@@ -118,7 +130,7 @@ export default function Home(ctx) {
pic={el.picture}
link={el.github}
key={i}
tabIndex={(Math.round(ar.length / 4) >= i) - 1}
tabIndex={Math.round(ar.length / 4) >= i ? 0 : 1}
/>
);
});
@@ -130,7 +142,7 @@ export default function Home(ctx) {
let ar = locale.specialThanks.people;
ar = ar.slice(Math.round(ar.length / 2));
ar = ar.concat(ar).concat(ar);
return ar.map((el, i) => {
return ar.map((el: TeamMemberObject, i: number) => {
return (
<TeamCard
compact
@@ -140,7 +152,7 @@ export default function Home(ctx) {
pic={el.picture}
link={el.github}
key={i}
tabIndex={(Math.round(ar.length / 4) >= i) - 1}
tabIndex={Math.round(ar.length / 4) >= i ? 0 : 1}
/>
);
});

View File

@@ -3,6 +3,8 @@
import { useState } from 'react';
import classNames from 'classnames';
import { CSSProperties, MouseEventHandler, AnimationEventHandler } from 'react';
import styles from './Bandwidth.module.css';
/**
@@ -20,10 +22,24 @@ import styles from './Bandwidth.module.css';
*
*/
export default function Bandwidth(ctx) {
interface VanillaBandwidthProps {
size?: number;
className?: string;
style?: CSSProperties;
onClick?: MouseEventHandler<SVGSVGElement>;
onAnimationEnd?: AnimationEventHandler<SVGSVGElement>;
}
interface BandwidthProps extends VanillaBandwidthProps {
lines?: Array<string>;
alwaysShowBubble?: boolean;
alignBubble?: 'left' | 'center' | 'right';
}
export default function Bandwidth(ctx: BandwidthProps) {
const { lines = [], size = 192, alwaysShowBubble = false, alignBubble = 'center', className, style } = ctx;
const [lineIndex, setLineIndex] = useState(-1 + alwaysShowBubble);
const [lineIndex, setLineIndex] = useState(-1 + +alwaysShowBubble);
const [jump, setJump] = useState(false || alwaysShowBubble);
const active = lineIndex > -1;
@@ -49,7 +65,7 @@ export default function Bandwidth(ctx) {
)}
<VanillaBandwidth
size={size}
onClick={updateLine}
onClick={() => updateLine()}
className={classNames(styles.bandwidth, { [styles.jump]: jump })}
onAnimationEnd={() => setJump(false)}
/>
@@ -70,12 +86,11 @@ export default function Bandwidth(ctx) {
*
*/
export const VanillaBandwidth = (ctx) => {
export const VanillaBandwidth = (ctx: VanillaBandwidthProps) => {
const { size = 192, className, style, onClick, onAnimationEnd } = ctx;
return (
<svg
xmlns="http://www.w3.org/2000/svg"
alt=""
version="1.1"
viewBox="0 0 813 813"
width={size}
@@ -85,6 +100,8 @@ export const VanillaBandwidth = (ctx) => {
onClick={onClick}
onAnimationEnd={onAnimationEnd}
>
<title>Bandwidth</title>
<desc>A drawing of Bandwidth, a purple raccoon with a brick in one paw and a Gamepad in the other.</desc>
<defs>
<style>
{

View File

@@ -2,6 +2,8 @@ import classNames from 'classnames';
import CoolHover from '@/components/CoolHover/CoolHover';
import styles from './Button.module.css';
import { ReactNode, MouseEventHandler, CSSProperties } from 'react';
import Link from 'next/link';
/**
@@ -20,7 +22,19 @@ import Link from 'next/link';
*
*/
export default function Button(ctx) {
interface ButtonProps {
children: ReactNode | string;
className?: string;
primary?: boolean;
icon?: boolean;
compact?: boolean;
onClick?: MouseEventHandler<HTMLButtonElement>;
href?: string;
target?: string;
style?: CSSProperties;
}
export default function Button(ctx: ButtonProps) {
const { children, className, primary, icon, compact, onClick, href, target, style } = ctx;
const BtnEl = () => {
@@ -50,7 +64,7 @@ export default function Button(ctx) {
};
return href ? (
<Link href={href} target={target} tabIndex="-1">
<Link href={href} target={target} tabIndex={-1}>
<BtnEl />
</Link>
) : (

View File

@@ -1,6 +1,8 @@
import classNames from 'classnames';
import styles from './ButtonWidget.module.css';
import { ReactNode, MouseEventHandler, CSSProperties } from 'react';
import Button from '@/components/Button/Button';
/**
@@ -19,8 +21,19 @@ import Button from '@/components/Button/Button';
*
*/
export default function ButtonWidget(ctx) {
const { children, className, primary, buttonText, onButtonClick, buttonHref, buttonTarget, style } = ctx;
interface ButtonWidgetProps {
children: ReactNode | string;
className?: string;
primary?: boolean;
buttonText: string;
onButtonClick?: MouseEventHandler<HTMLButtonElement>;
buttonHref?: string;
buttonTarget?: string;
style?: CSSProperties;
}
export default function ButtonWidget(ctx: ButtonWidgetProps) {
const { children, className, primary, buttonText, onButtonClick, buttonHref, buttonTarget, style = {} } = ctx;
return (
<div
className={classNames(styles.widget, className)}

View File

@@ -1,6 +1,7 @@
import styles from './Caption.module.css';
import classNames from 'classnames';
import { ReactNode, CSSProperties } from 'react';
/**
* A reusable component for captions.
@@ -12,7 +13,14 @@ import classNames from 'classnames';
*
*/
export default function Caption(ctx) {
interface CaptionProps {
children: string;
center?: boolean;
style?: CSSProperties;
id?: string;
}
export default function Caption(ctx: CaptionProps) {
const { children: caption, center, style, id } = ctx;
return (

View File

@@ -1,6 +1,6 @@
'use client';
import { useState } from 'react';
import { useState, ReactNode, CSSProperties } from 'react';
import styles from './CoolHover.module.css';
import classNames from 'classnames';
@@ -20,7 +20,16 @@ import classNames from 'classnames';
*
*/
export default function CoolHover(ctx) {
interface CoolHoverProps {
hoverColor?: string;
bgColor?: string;
children: ReactNode;
className?: string;
radius?: number;
style?: CSSProperties;
}
export default function CoolHover(ctx: CoolHoverProps) {
const { hoverColor, bgColor, children, className, radius, style } = ctx;
const [pos, setPos] = useState({ x: 0, y: 0 });

View File

@@ -1,6 +1,6 @@
'use client';
import { useEffect, useRef, useMemo } from 'react';
import { useEffect, useRef, useMemo, ReactNode, RefObject, Dispatch, SetStateAction } from 'react';
import classNames from 'classnames';
import styles from './Dropdown.module.css';
@@ -42,7 +42,20 @@ import styles from './Dropdown.module.css';
* />
*/
export default function Dropdown(ctx) {
interface DropdownContentObject {
el: ReactNode;
tr: RefObject<ReactNode>;
}
interface DropdownProps {
dropdownContents: Array<DropdownContentObject>;
openState: [number, Dispatch<SetStateAction<number>>];
clickedState: [boolean, Dispatch<SetStateAction<boolean>>];
boundaryRef: RefObject<HTMLDivElement>;
boundaryTolerance: number;
}
export default function Dropdown(ctx: DropdownProps) {
const [open, setOpen] = ctx.openState;
const [clicked, setClicked] = ctx.clickedState;
const { boundaryRef, boundaryTolerance } = ctx;
@@ -68,36 +81,34 @@ export default function Dropdown(ctx) {
const dropdownContentRect = dropdownContent?.current?.getBoundingClientRect();
const dropdownRect = dropdownContent?.current?.getBoundingClientRect();
const pxToInt = (px) => parseInt(px.replace('px', ''));
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
const pxToInt = (px: string) => parseInt(px.replace('px', ''));
const clamp = (num: number, min: number, max: number) => Math.min(Math.max(num, min), max);
function getRealCenter(trRef: RefObject<HTMLDivElement>) {
const style = getComputedStyle(trRef.current);
const rightPad = pxToInt(style.paddingRight);
const leftPad = pxToInt(style.paddingLeft);
const elWidth = pxToInt(style.width);
return (elWidth - rightPad - leftPad) / 2;
}
function getTriggerOffset(trRef: RefObject<HTMLDivElement>) {
const style = getComputedStyle(trRef.current);
const leftPad = pxToInt(style.paddingLeft);
return (
trRef.current.getBoundingClientRect().left - boundaryRef.current.getBoundingClientRect().left + leftPad
);
}
function getLeftOffset(trRef: RefObject<HTMLDivElement>) {
return getTriggerOffset(trRef) + getRealCenter(trRef);
}
if (dropdownContentRect && open) {
const sectionWidth = pxToInt(getComputedStyle(boundaryRef.current).width);
const dropdownWidth = dropdownRect.width;
function getRealCenter(trRef) {
const style = getComputedStyle(trRef.current);
const rightPad = pxToInt(style.paddingRight);
const leftPad = pxToInt(style.paddingLeft);
const elWidth = pxToInt(style.width);
return (elWidth - rightPad - leftPad) / 2;
}
function getTriggerOffset(trRef) {
const style = getComputedStyle(trRef.current);
const leftPad = pxToInt(style.paddingLeft);
return (
trRef.current.getBoundingClientRect().left -
boundaryRef.current.getBoundingClientRect().left +
leftPad
);
}
function getLeftOffset(trRef) {
return getTriggerOffset(trRef) + getRealCenter(trRef);
}
const arrowOffset = getLeftOffset(dropdownTriggers[open]);
const leftOffset = getLeftOffset(dropdownTriggers[open]) - dropdownWidth / 2;
@@ -118,8 +129,8 @@ export default function Dropdown(ctx) {
dropdown.current.style.height = 0;
}
const closeOnClickOutside = (e) => {
if (dropdown.current && !dropdown.current.contains(e.target)) {
const closeOnClickOutside = (e: MouseEvent) => {
if (dropdown.current && !dropdown.current.contains(e.target as Node)) {
setOpen(0);
setClicked(false);
}

View File

@@ -6,7 +6,16 @@ import { CaretRight } from '@phosphor-icons/react/dist/ssr';
import styles from './Faq.module.css';
export default function Faq(ctx) {
interface QAObject {
question: string;
answer: string;
}
interface FaqProps {
questionObject: Array<QAObject>;
}
export default function Faq(ctx: FaqProps) {
const { questionObject } = ctx;
const [selectedIndex, setSelectedIndex] = useState(0);
@@ -22,7 +31,7 @@ export default function Faq(ctx) {
className={classNames({ [styles.selected]: selected })}
onClick={() => setSelectedIndex(i)}
onKeyDown={(e) => e.key === 'Enter' && setSelectedIndex(i)}
tabIndex="0"
tabIndex={0}
>
<CaretRight size={24} weight="bold" className={styles.chevron} alt="" />
{question.question}

View File

@@ -11,7 +11,7 @@ import Link from 'next/link';
import styles from './Footer.module.css';
export default function Footer(ctx) {
export default function Footer() {
const { locale } = getLocale('TODO');
const year = new Date().getFullYear(); /* Can't have an outdated year this way :3 */
@@ -28,7 +28,7 @@ export default function Footer(ctx) {
href={`https://wikipedia.org/wiki/${year}`}
target="_blank"
className={styles.link}
tabIndex="-1"
tabIndex={-1}
>
{year}
</Link>

View File

@@ -1,4 +1,4 @@
import ImageFix from '@/components/ImageFix/ImageFix';
import Image from 'next/image';
import n2ds from '/public/assets/images/n2ds.png';
import Title from '@/components/Title/Title';
@@ -22,13 +22,7 @@ export default function Hero({ locale }) {
</Button>
<div>
<Button
icon
href="https://discord.gg/pretendo"
target="_blank"
aria-label="Discord server"
title="Discord server"
>
<Button icon href="https://discord.gg/pretendo" target="_blank" aria-label="Discord server">
<DiscordLogo size={32} weight="fill" alt="" />
</Button>
@@ -41,7 +35,7 @@ export default function Hero({ locale }) {
<GithubLogo size={32} weight="fill" alt="" />
</Button>
<Button icon href="/account/upgrade" target="_blank" title="Upgrade page">
<Button icon href="/account/upgrade" target="_blank" aria-label="Upgrade page">
<Heart size={32} weight="fill" alt="" />
</Button>
</div>
@@ -50,7 +44,7 @@ export default function Hero({ locale }) {
<div className={styles.images}>
<div className={styles.lightCircle}>
<svg
alt=""
aria-hidden={true}
xmlns="http://www.w3.org/2000/svg"
width="839.371"
height="893.406"
@@ -155,7 +149,7 @@ export default function Hero({ locale }) {
</g>
</svg>
<ImageFix className={styles.n2ds} src={n2ds} alt="" />
<Image className={styles.n2ds} src={n2ds} alt="" />
</div>
</div>
</div>

View File

@@ -1,12 +0,0 @@
import Image from 'next/image';
// stolen from https://github.com/prismicio/prismic-next/pull/79
// TODO: Remove once https://github.com/vercel/next.js/issues/52216 is resolved.
// `next/image` seems to be affected by a default + named export bundling bug.
let ImageFix = Image;
if ('default' in ImageFix) {
ImageFix = ImageFix.default;
}
export default ImageFix;

View File

@@ -1,3 +1,5 @@
import { CSSProperties } from 'react';
/**
* The Pretendo P, with or without text.
*
@@ -12,14 +14,21 @@
*
*/
export default function Logo(ctx) {
interface LogoProps {
text?: boolean;
size?: number;
center?: boolean;
className?: string;
style?: CSSProperties;
}
export default function Logo(ctx: LogoProps) {
const { text, size = 48, center, className, style } = ctx;
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox={text ? '0 0 120 39.88' : '0 0 39.88 39.88'}
aria-label="Pretendo"
title="Pretendo"
height={size}
width={text ? size * 3 : size}
className={className}

View File

@@ -1,5 +1,7 @@
import classNames from 'classnames';
import { CSSProperties, ReactNode, RefObject } from 'react';
import styles from './Section.module.css';
/**
@@ -18,7 +20,17 @@ import styles from './Section.module.css';
*
*/
export default function Section(ctx) {
interface SectionProps {
id?: string;
style?: CSSProperties;
children?: ReactNode;
className?: string;
compact?: boolean;
contentClassName?: string;
contentRef?: RefObject<HTMLDivElement>;
}
export default function Section(ctx: SectionProps) {
const { id, style, children, className, compact, contentClassName, contentRef } = ctx;
return (

View File

@@ -1,12 +1,19 @@
import styles from './ShowcaseSection.module.css';
import classNames from 'classnames';
import ImageFix from '@/components/ImageFix/ImageFix';
import Image, { StaticImageData } from 'next/image';
import Section from '@/components/Section/Section';
import Title from '@/components/Title/Title';
export default function ShowcaseSection(ctx) {
interface ShowcaseSectionProps {
title?: string;
caption?: string;
image?: StaticImageData;
isOdd?: boolean
}
export default function ShowcaseSection(ctx: ShowcaseSectionProps) {
const { title, caption, image, isOdd } = ctx;
return (
<Section
@@ -17,7 +24,7 @@ export default function ShowcaseSection(ctx) {
<Title>{title}</Title>
<p className={styles.caption}>{caption}</p>
</div>
<ImageFix src={image} alt="" className={styles.image} quality={100} sizes="(max-width: 840px) 100vw, 700px" />
<Image src={image} alt="" className={styles.image} quality={100} sizes="(max-width: 840px) 100vw, 700px" />
</Section>
);
}

View File

@@ -1,5 +1,5 @@
import styles from './TeamCard.module.css';
import ImageFix from '@/components/ImageFix/ImageFix';
import Image, { StaticImageData } from 'next/image';
import Link from 'next/link';
import { Star } from '@phosphor-icons/react/dist/ssr';
@@ -21,7 +21,18 @@ import classNames from 'classnames';
*
*/
export default function TeamCard(ctx) {
interface TeamCardProps {
name: string;
caption: string;
pic: StaticImageData | string;
link?: string;
compact?: boolean;
special?: boolean;
className?: string;
tabIndex?: number;
}
export default function TeamCard(ctx: TeamCardProps) {
const { name, caption, pic, link, compact, special, className, tabIndex } = ctx;
return (
@@ -35,7 +46,7 @@ export default function TeamCard(ctx) {
tabIndex={tabIndex}
>
<div className={styles.imageWrapper}>
<ImageFix src={pic} width={110} height={110} alt="" className={styles.image} />
<Image src={pic} width={110} height={110} alt="" className={styles.image} />
</div>
<div className={styles.text}>
<h3>{name}</h3>

View File

@@ -1,4 +1,4 @@
import { createElement } from 'react';
import { createElement, ReactNode, CSSProperties } from 'react';
import classNames from 'classnames';
import styles from './Title.module.css';
@@ -13,7 +13,16 @@ import styles from './Title.module.css';
*
*/
export default function Title(ctx) {
interface TitleProps {
children: string;
className?: string;
element?: string;
style?: CSSProperties;
id?: string;
center?: boolean;
}
export default function Title(ctx: TitleProps) {
const { children: title = '', className, element, style, id, center } = ctx;
// regex to match all start/end punctuation except for parentheses and brackets. Please never make me do this again.

View File

@@ -1,10 +1,10 @@
import fs from 'fs-extra';
import merge from 'lodash.merge';
import logger from './logger.js';
import logger from './logger.ts';
import baseLocale from '@/locales/en_US.json' assert { type: 'json' };
function getLocale(code) {
function getLocale(code: string) {
const localeCode = code.replace('-', '_');
const path = `@/locales/${localeCode}.json`;
@@ -26,7 +26,9 @@ function getLocale(code) {
}
function getLocaleList() {
const filenames = fs.readdirSync('@/locales').map((filename) => filename.replace('.json', '').replace('_', '-'));
const filenames = fs
.readdirSync('@/locales')
.map((filename: string) => filename.replace('.json', '').replace('_', '-'));
return filenames;
}

View File

@@ -13,31 +13,37 @@ const writeStreams = {
const time = () => {
const date = new Date();
return date.toLocaleTimeString([], { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 });
return date.toLocaleTimeString([], {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
fractionalSecondDigits: 3,
});
};
function success(message) {
function success(message: string) {
const logLine = `[${time()}] [SUCCESS]: ${message}`;
writeStreams.success.write(`${logLine}\n`);
console.log(chalk.green.bold(logLine));
}
function error(message) {
function error(message: string) {
const logLine = `[${time()}] [ERROR]: ${message}`;
writeStreams.error.write(`${logLine}\n`);
console.log(chalk.red.bold(logLine));
}
function warn(message) {
function warn(message: string) {
const logLine = `[${time()}] [WARN]: ${message}`;
writeStreams.warn.write(`${logLine}\n`);
console.log(chalk.yellow.bold(logLine));
}
function info(message) {
function info(message: string) {
const logLine = `[${time()}] [INFO]: ${message}`;
writeStreams.info.write(`${logLine}\n`);

32
tsconfig.json Normal file
View File

@@ -0,0 +1,32 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"baseUrl": ".",
"paths": {
"@/components/*": ["./src/components/*"],
"@/utils/*": ["./src/utils/*"],
"@/public/*": ["./public/*"],
"@/locales/*": ["./locales/*"],
"@/app/*": ["./src/app/*"]
},
"allowJs": true,
"allowImportingTsExtensions": true,
"skipLibCheck": true,
"strict": false,
"noEmit": true,
"incremental": true,
"module": "esnext",
"esModuleInterop": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}