feat(ui): add GoBackButton component with animated hover and history check
All checks were successful
Deploy Flowstate to VPS / deploy (push) Successful in 59s
All checks were successful
Deploy Flowstate to VPS / deploy (push) Successful in 59s
This commit is contained in:
parent
60e2b77324
commit
d6209c900d
4 changed files with 95 additions and 13 deletions
|
|
@ -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" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
85
app/components/ui/buttons/GoBackButton.tsx
Normal file
85
app/components/ui/buttons/GoBackButton.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
Loading…
Reference in a new issue