84 lines
2.3 KiB
TypeScript
84 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import { useActionState } from "react";
|
|
import { useFormStatus } from "react-dom";
|
|
import { subscribe, type SubscribeState } from "@/app/actions/subscribe";
|
|
|
|
const initialState: SubscribeState = { status: "idle", message: "" };
|
|
|
|
function SubmitButton() {
|
|
const { pending } = useFormStatus();
|
|
return (
|
|
<button
|
|
type="submit"
|
|
disabled={pending}
|
|
className="px-6 py-3 bg-accent text-background font-display tracking-widest text-sm hover:bg-foreground hover:scale-[1.03] transition duration-200 disabled:opacity-60 disabled:cursor-not-allowed disabled:hover:scale-100"
|
|
>
|
|
{pending ? "SENDING…" : "SUBSCRIBE"}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
type Props = {
|
|
variant?: "default" | "compact";
|
|
};
|
|
|
|
export function Newsletter({ variant = "default" }: Props) {
|
|
const [state, formAction] = useActionState(subscribe, initialState);
|
|
|
|
const wrapperClass =
|
|
variant === "compact"
|
|
? "w-full max-w-lg"
|
|
: "w-full max-w-lg mx-auto";
|
|
|
|
return (
|
|
<form
|
|
action={formAction}
|
|
className={wrapperClass}
|
|
aria-describedby="newsletter-status"
|
|
>
|
|
{/* honeypot for bots */}
|
|
<input
|
|
type="text"
|
|
name="website"
|
|
tabIndex={-1}
|
|
autoComplete="off"
|
|
className="hidden"
|
|
aria-hidden
|
|
/>
|
|
<label htmlFor="newsletter-email" className="sr-only">
|
|
Email address
|
|
</label>
|
|
<div className="flex flex-col sm:flex-row gap-3">
|
|
<input
|
|
id="newsletter-email"
|
|
type="email"
|
|
name="email"
|
|
required
|
|
placeholder="your@email.com"
|
|
className="flex-1 min-w-0 bg-surface border border-border px-4 py-3 text-sm text-foreground placeholder:text-foreground-muted focus:outline-none focus:border-accent transition-colors"
|
|
/>
|
|
<SubmitButton />
|
|
</div>
|
|
|
|
<p
|
|
id="newsletter-status"
|
|
role="status"
|
|
aria-live="polite"
|
|
className={`text-xs tracking-widest mt-3 ${
|
|
variant === "compact" ? "" : "text-center"
|
|
} ${
|
|
state.status === "success"
|
|
? "text-accent"
|
|
: state.status === "error"
|
|
? "text-red-400"
|
|
: "text-foreground-muted"
|
|
}`}
|
|
>
|
|
{state.message ||
|
|
"No spam. One devblog email per major update. Unsubscribe anytime."}
|
|
</p>
|
|
</form>
|
|
);
|
|
}
|