feat(ui): add GoBackButton component with animated hover and history check
All checks were successful
Deploy Flowstate to VPS / deploy (push) Successful in 59s

This commit is contained in:
Chneemann 2026-08-16 19:19:29 +02:00
parent 60e2b77324
commit d6209c900d
No known key found for this signature in database
4 changed files with 95 additions and 13 deletions

View file

@ -5,8 +5,8 @@
"use client";
import { ArrowLeft, Trash2 } from "lucide-react";
import Link from "next/link";
import { GoBackButton } from "@/app/components/ui/buttons/GoBackButton";
import { Trash2 } from "lucide-react";
/**
* Renders the trash page header featuring title details, an indicator icon,
@ -29,16 +29,7 @@ export default function TrashHeader() {
</div>
</div>
<Link
href="/dashboard"
className="inline-flex items-center gap-2 text-xs text-primary hover:text-primary-hover transition-colors mb-6 group"
>
<ArrowLeft
size={14}
className="group-hover:-translate-x-1 transition-transform"
/>
Back to Dashboard
</Link>
<GoBackButton fallbackUrl="/dashboard" label="Dashboard" />
</>
);
}

View file

@ -0,0 +1,85 @@
/**
* @file components/ui/buttons/GoBackButton.tsx
* @description Client component rendering an interactive back button with intelligent history checking and animated hover effects.
*/
"use client";
import { useRouter } from "next/navigation";
import { ArrowLeft } from "lucide-react";
import { ComponentPropsWithoutRef } from "react";
/**
* Properties for the GoBackButton component.
*
* @interface GoBackButtonProps
* @extends {ComponentPropsWithoutRef<"button">}
* @property {string} [fallbackUrl] - The backup route URL to navigate to if no browser history exists.
* @property {string} [label] - The text label displayed next to the arrow icon.
*/
interface GoBackButtonProps extends ComponentPropsWithoutRef<"button"> {
fallbackUrl?: string;
label?: string;
}
/**
* Renders a back button that navigates to the previous history entry if available,
* or falls back to a default URL, featuring an animated left arrow icon and underline effects.
*
* @param {GoBackButtonProps} props - The component props.
* @returns {JSX.Element} The rendered go back button component.
*/
export function GoBackButton({
fallbackUrl = "/",
label = "previous page",
className = "",
onClick,
...props
}: GoBackButtonProps) {
const router = useRouter();
/**
* Handles the click event, triggering custom click actions if provided,
* and determining whether to pop history or redirect to the fallback URL.
*
* @param {React.MouseEvent<HTMLButtonElement>} e - The mouse event object.
*/
const handleBack = (e: React.MouseEvent<HTMLButtonElement>) => {
if (onClick) {
onClick(e);
}
if (e.defaultPrevented) return;
e.preventDefault();
const hasHistory = window.history.state && window.history.state.idx > 0;
if (hasHistory) {
router.back();
} else {
router.push(fallbackUrl);
}
};
return (
<button
type="button"
onClick={handleBack}
className={`inline-flex items-center gap-2 text-xs text-primary hover:text-primary-hover transition-colors group bg-transparent border-none p-0 cursor-pointer ${className}`}
{...props}
>
<ArrowLeft
size={14}
aria-hidden="true"
className="group-hover:-translate-x-1 transition-transform shrink-0"
/>
<span className="relative pb-0.5">
Back to {label}
<span
className="absolute left-0 bottom-0 h-px w-0 bg-primary transition-all duration-300 ease-in-out group-hover:w-full"
aria-hidden="true"
/>
</span>
</button>
);
}

View file

@ -3,6 +3,8 @@
* @description Server component rendering the legal notice (imprint) page with a title header and back button navigation.
*/
import { GoBackButton } from "../components/ui/buttons/GoBackButton";
/**
* Renders the imprint/legal notice page containing the main title and layout container.
*
@ -16,6 +18,7 @@ export default async function ImprintPage() {
<h1 className="text-2xl font-bold text-foreground mb-1 tracking-tight">
Legal Notice<span className="text-primary">.</span>
</h1>
<GoBackButton />
<div className="space-y-6 text-sm mt-3 text-foreground-muted leading-relaxed">
{/* Information in accordance with § 5 DDG & § 18 MStV */}
<div className="rounded-2xl border border-border bg-card p-6 shadow-xl space-y-3">
@ -23,7 +26,7 @@ export default async function ImprintPage() {
<span className="text-primary">//</span> Information Pursuant to §
5 DDG
</h2>
<div className="font-mono text-xs bg-background/60 p-4 rounded-xl border border-border text-foreground space-y-1">
<div className="font-mono text-xs bg-background/60 p-4 rounded-xl border border-border text-foreground">
<p className="font-semibold">André Kempf</p>
<p className="text-primary-hover">Full-Stack Web Developer</p>
<p>Großschneidersweg 2a</p>

View file

@ -3,6 +3,8 @@
* @description Server component rendering the privacy policy page with a title header and back button navigation.
*/
import { GoBackButton } from "../components/ui/buttons/GoBackButton";
/**
* Renders the privacy policy page containing the main title and layout container alongside a back navigation button.
*
@ -16,6 +18,7 @@ export default async function PrivacyPage() {
<h1 className="text-2xl font-bold text-foreground mb-1 tracking-tight">
Privacy Policy<span className="text-primary">.</span>
</h1>
<GoBackButton />
<div className="space-y-6 text-sm mt-3 text-foreground-muted leading-relaxed">
{/* Overview */}
<div className="rounded-2xl border border-border bg-card p-6 shadow-xl space-y-4">