Skip to content

Commit f38ce9c

Browse files
committed
Merge branch 'development' into feature/error-handling
2 parents 418b888 + 01d66a1 commit f38ce9c

File tree

7 files changed

+142
-25
lines changed

7 files changed

+142
-25
lines changed

src/components/controls/Button.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ButtonHTMLAttributes, DetailedHTMLProps, HTMLAttributes, useMemo } from "react";;
1+
import { ButtonHTMLAttributes, DetailedHTMLProps, ForwardedRef, forwardRef, HTMLAttributes, useMemo } from "react";;
22
import { cva, VariantProps as GetVariantProps } from "class-variance-authority";
33
import { twMerge } from "tailwind-merge";
44

@@ -19,16 +19,16 @@ type ButtonProps = {
1919
} & RequiredKeys<VariantProps, "color">
2020
& DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
2121

22-
const Button = ({ className, color, ...props }: ButtonProps) => {
22+
const Button = forwardRef(({ className, color, ...props }: ButtonProps, ref: ForwardedRef<HTMLButtonElement>) => {
2323

2424
const computedClassName = useMemo(
2525
() => twMerge(style({ color }), className),
2626
[className, color]
2727
);
2828

2929
return (
30-
<button className={computedClassName} {...props} />
30+
<button ref={ref} className={computedClassName} {...props} />
3131
);
32-
};
32+
});
3333

3434
export default Button;

src/components/navigation/Footer.tsx

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,87 @@
1-
import React from "react";
21
import Link from "./Link";
32

3+
import Logo from "@/assets/images/brand/logo-200x200.webp";
4+
45
interface FooterProps {
56

67
}
78

89
const Footer = ({ }: FooterProps) => {
910
return (
10-
<footer className="flex gap-4 p-4 bg-primary text-primary-contrast">
11-
<div>
12-
<p className="text-lg font-semibold text-center">Contact:</p>
13-
<div className="flex gap-2">
14-
<span className="font-semibold">Feedback:</span>
15-
<Link color="white" href="mailto:feedback@commitrocket.com" underline>feedback@commitrocket.com</Link>
11+
<footer className="flex items-center justify-center text-primary-contrast">
12+
<div className="flex flex-col w-full gap-4 p-8 max-w-7xl rounded-t-md bg-primary">
13+
<div className="flex flex-col items-center gap-2 p-2 rounded-md bg-white/20 sm:flex-row">
14+
<div className="flex items-center gap-2">
15+
<img
16+
className="w-12 h-12"
17+
src={Logo.src}
18+
width={Logo.width}
19+
height={Logo.height}
20+
/>
21+
<span className="font-semibold text-black">
22+
Commit Rocket
23+
</span>
24+
</div>
25+
<span className="hidden text-black/50 sm:block">
26+
-
27+
</span>
28+
<div className="text-black/50">
29+
© Rik den Breejen
30+
</div>
1631
</div>
17-
</div>
18-
<div>
19-
<p className="text-lg font-semibold text-center">Contact:</p>
20-
<div className="flex gap-2">
21-
<span className="font-semibold">Feedback:</span>
22-
<Link color="white" href="mailto:feedback@commitrocket.com" underline>feedback@commitrocket.com</Link>
32+
<div className="flex flex-col justify-between w-full gap-8 md:flex-row">
33+
<div className="flex flex-col">
34+
<p className="mb-2 text-lg font-semibold">Commit Rocket</p>
35+
<Link
36+
color="white"
37+
href="/blog"
38+
title="Commit Rocket Blog"
39+
underline
40+
>
41+
Blog
42+
</Link>
43+
<Link
44+
color="white"
45+
href="https://github.com/commit-rocket/commit-rocket"
46+
title="Commit Rocket Source-Code"
47+
underline
48+
external
49+
>
50+
Github
51+
</Link>
52+
</div>
53+
<div className="flex flex-col">
54+
<p className="mb-2 text-lg font-semibold">Website</p>
55+
<Link
56+
color="white"
57+
href="https://github.com/commit-rocket/commit-rocket-website"
58+
title="Commit Rocket Website Source-Code"
59+
underline
60+
external
61+
>
62+
Frontend Source
63+
</Link>
64+
<Link
65+
color="white"
66+
href="https://github.com/commit-rocket/commit-rocket-website-backend"
67+
title="Commit Rocket Website Backend Source-Code"
68+
underline
69+
external
70+
>
71+
Backend Source
72+
</Link>
73+
</div>
74+
<div className="flex flex-col">
75+
<p className="mb-2 text-lg font-semibold">More</p>
76+
<Link
77+
color="white"
78+
href="/about"
79+
title="About the creators of Commit Rocket"
80+
underline
81+
>
82+
About Us
83+
</Link>
84+
</div>
2385
</div>
2486
</div>
2587
</footer>

src/components/navigation/Header.tsx

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReactNode, useState } from "react";
1+
import { ReactNode, useRef, useState } from "react";
22
import { useRouter } from "next/router";
33
import Bars3Icon from "@heroicons/react/24/solid/Bars3Icon";
44

@@ -7,12 +7,20 @@ import Logo from "@/assets/images/brand/logo-200x200.webp";
77
import Link from "./Link";
88
import Button from "../controls/Button";
99
import NavLink from "./NavLink";
10+
import useOutsideClick from "@/hooks/useOutsideClick";
1011

1112
const Header = () => {
1213
const router = useRouter();
1314

15+
const buttonRef = useRef<HTMLButtonElement>(null);
16+
const itemsContainerRef = useRef<HTMLDivElement>(null);
17+
1418
const [open, setOpen] = useState(false);
1519

20+
useOutsideClick([buttonRef, itemsContainerRef], (e) => {
21+
setOpen(false);
22+
});
23+
1624
return (
1725
<header className="relative flex items-center w-full gap-4 p-4 transition-all md:px-8 md:gap-12">
1826
<Link color="primary" className="flex items-center justify-center text-2xl font-bold transition-all lg:text-4xl" href="/">
@@ -25,10 +33,22 @@ const Header = () => {
2533
/>
2634
<span>Commit Rocket</span>
2735
</Link>
28-
<Button color="secondary" className="p-2 ml-auto rounded-full md:hidden" onClick={() => setOpen(!open)}>
36+
<Button
37+
ref={buttonRef}
38+
className="p-2 ml-auto rounded-full md:hidden"
39+
color="secondary"
40+
aria-expanded={open}
41+
aria-controls="header-items"
42+
onClick={() => setOpen(!open)}
43+
>
2944
<Bars3Icon className="w-6 h-6" />
3045
</Button>
31-
<div aria-expanded={open} className="absolute flex flex-col bg-fill gap-0 p-4 top-full inset-x-4 rounded-md shadow shadow-primary z-10 aria-[expanded='false']:hidden md:aria-[expanded='false']:flex md:flex-row md:items-center md:p-0 md:shadow-none md:static md:bg-transparent md:gap-12">
46+
<div
47+
ref={itemsContainerRef}
48+
className="absolute flex flex-col bg-fill gap-0 p-4 top-full inset-x-4 rounded-md shadow shadow-primary z-10 data-[expanded='false']:hidden md:data-[expanded='false']:flex md:flex-row md:items-center md:p-0 md:shadow-none md:static md:bg-transparent md:gap-12"
49+
id="header-items"
50+
data-expanded={open}
51+
>
3252
<NavLink href="/" currentHref={router.pathname}>
3353
Home
3454
</NavLink>

src/components/navigation/Link.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useMemo } from "react";
22
import InternalLink, { LinkProps as InternalLinkProps } from "next/link";
33
import { cva, VariantProps as GetVariantProps } from "class-variance-authority";
44
import { twMerge } from "tailwind-merge";
5+
import ArrowTopRightOnSquareIcon from "@heroicons/react/24/solid/ArrowTopRightOnSquareIcon";
56

67
import { RequiredKeys } from "@/types/utility";
78

@@ -26,20 +27,26 @@ export type VariantProps = GetVariantProps<typeof style>;
2627

2728
type LinkProps = {
2829
underline?: boolean;
30+
external?: boolean;
2931
} & RequiredKeys<VariantProps, "color">
3032
& Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof InternalLinkProps>
3133
& InternalLinkProps
3234
& React.RefAttributes<HTMLAnchorElement>;
3335

34-
const Link = ({ className, color, underline, ...props }: LinkProps) => {
36+
const Link = ({ className, color, underline, children, external, hrefLang = "en", ...props }: LinkProps) => {
3537

3638
const computedClassName = useMemo(
3739
() => twMerge(style({ color, underline }), className),
3840
[className, color, underline]
3941
);
4042

4143
return (
42-
<InternalLink className={computedClassName} {...props} />
44+
<InternalLink className={computedClassName} hrefLang={hrefLang} {...props}>
45+
{children}
46+
{external && <ArrowTopRightOnSquareIcon
47+
className="inline w-4 h-4"
48+
/>}
49+
</InternalLink>
4350
);
4451
};
4552

src/hooks/useOutsideClick.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { MutableRefObject, useEffect, useRef, RefObject } from "react";
2+
3+
const useOutsideClick = (refs: (RefObject<Element> | MutableRefObject<Element>)[], handler: (event: TouchEvent | MouseEvent) => void) => {
4+
const handlerRef = useRef(handler);
5+
handlerRef.current = handler;
6+
7+
useEffect(() => {
8+
const listener = (event: TouchEvent | MouseEvent) => {
9+
const isContained = refs.some((ref) => {
10+
if (!event.target || !ref.current) return true;
11+
return ref.current.contains(event.target as Element);
12+
});
13+
if (isContained) return;
14+
15+
handlerRef.current(event);
16+
};
17+
18+
document.addEventListener("mousedown", listener);
19+
document.addEventListener("touchstart", listener);
20+
21+
return () => {
22+
document.removeEventListener("mousedown", listener);
23+
document.removeEventListener("touchstart", listener);
24+
};
25+
}, [...refs]);
26+
};
27+
28+
export default useOutsideClick;

src/pages/404.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const NotFound: Page = ({ }) => {
1616
<Head>
1717
<title>404 - Commit Rocket</title>
1818
</Head>
19-
<main className="flex items-center justify-center flex-1 w-full" aria-labelledby="not-found">
19+
<main className="flex items-center justify-center flex-1 w-full pb-8" aria-labelledby="not-found">
2020
<div className="flex flex-col gap-2 p-4 text-center rounded-md shadow shadow-black/25 bg-primary/25">
2121
<h1 id="not-found" className="text-5xl text-secondary">Not Found.</h1>
2222
<p>There are no commits to be found here!</p>

src/pages/mail/unsubscribe.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ const UnsubscribePage: Page = ({ }) => {
6161
<Head>
6262
<title>Commit Rocket</title>
6363
</Head>
64-
<main className="flex items-center justify-center flex-1 w-full" aria-labelledby="not-found">
64+
<main className="flex items-center justify-center flex-1 w-full pb-8" aria-labelledby="unsubscribe">
6565
<form className="flex flex-col gap-4 p-4 text-center rounded-md shadow shadow-black/25 bg-primary/25" onSubmit={submit}>
66-
<h1 id="not-found" className="text-5xl text-secondary">Unsubscribe</h1>
66+
<h1 id="unsubscribe" className="text-5xl text-secondary">Unsubscribe</h1>
6767
<p className="max-w-md">Once you unsubscribe you won't receive any more emails from us and your email will be immediately deleted from our records.</p>
6868
<AnimatePresence mode="wait">
6969
{(!response || response.success === false) && <motion.div

0 commit comments

Comments
 (0)