Files
Investidor-V2/resources/js/components/institutional-source/BackButton.tsx
2026-10-20 11:39:25 +02:00

157 lines
4.7 KiB
TypeScript

// Imported from investidor; return destinations are restricted to this platform.
import React, { useEffect, useState } from 'react';
import { router } from '@inertiajs/react';
import { ArrowLeft } from 'lucide-react';
import { Trans } from './source-text'; // Import Trans component
interface BackButtonProps {
to?: string;
label?: React.ReactNode; // Change type to React.ReactNode to accept <Trans> component
className?: string;
theme?: 'light' | 'dark';
onClick?: () => void;
}
const RETURN_URL_KEY = 'news:return-url';
function normalizeInternalUrl(value: string | null): string | null {
if (!value || typeof window === 'undefined') {
return null;
}
try {
const url = value.startsWith('http')
? new URL(value)
: new URL(value, window.location.origin);
if (url.origin !== window.location.origin) {
return null;
}
const normalized = `${url.pathname}${url.search}${url.hash}`;
const current = `${window.location.pathname}${window.location.search}${window.location.hash}`;
return normalized !== current ? normalized : null;
} catch {
return null;
}
}
function getStoredReturnUrl(): string | null {
try {
return normalizeInternalUrl(sessionStorage.getItem(RETURN_URL_KEY));
} catch {
return null;
}
}
function getReturnUrlFromPage(): string | null {
if (typeof window === 'undefined') {
return null;
}
const params = new URLSearchParams(window.location.search);
const fromParam = normalizeInternalUrl(params.get('from'));
if (fromParam) {
return fromParam;
}
return null;
}
function getInternalReferrerUrl(): string | null {
if (typeof document === 'undefined') {
return null;
}
return normalizeInternalUrl(document.referrer);
}
export const BackButton: React.FC<BackButtonProps> = ({
to,
label = <Trans>Voltar</Trans>, // Use Trans component for default label
className = '',
theme = 'light',
onClick,
}) => {
const [showFloatingButton, setShowFloatingButton] = useState(false);
useEffect(() => {
const handleScroll = () => {
setShowFloatingButton(window.scrollY > 220);
};
handleScroll();
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const themeClasses =
theme === 'dark'
? 'border-white/20 bg-white/10 text-white shadow-lg backdrop-blur-md hover:bg-white/20'
: 'border-red-100 bg-white text-[#ED1C24] shadow-sm hover:border-red-200 hover:bg-red-50';
const baseClasses =
'inline-flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-semibold transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[#ED1C24]/25 active:scale-[0.98]';
const floatingClasses = [
'fixed bottom-5 left-4 z-50 max-w-[calc(100vw-2rem)] sm:bottom-6 sm:left-6',
'border-red-100 bg-white text-[#ED1C24] shadow-xl ring-1 ring-black/5 hover:border-red-200 hover:bg-red-50',
showFloatingButton
? 'pointer-events-auto translate-y-0 opacity-100'
: 'pointer-events-none translate-y-3 opacity-0',
].join(' ');
const renderContent = () => (
<>
<ArrowLeft size={16} aria-hidden="true" />
<span className="truncate">{label}</span>
</>
);
const handleClick = () => {
if (onClick) {
onClick();
return;
}
const explicitReturnUrl = getReturnUrlFromPage();
const storedReturnUrl = getStoredReturnUrl();
if (explicitReturnUrl) {
router.visit(explicitReturnUrl);
} else if (storedReturnUrl) {
router.visit(storedReturnUrl);
} else {
router.visit(getInternalReferrerUrl() || to || '/');
}
};
return (
<>
<button
type="button"
onClick={handleClick}
className={`${baseClasses} ${themeClasses} ${className}`}
aria-label={typeof label === 'string' ? label : undefined} // aria-label should be a string
>
{renderContent()}
</button>
<button
type="button"
onClick={handleClick}
className={`${baseClasses} ${floatingClasses}`}
aria-label={typeof label === 'string' ? label : undefined} // aria-label should be a string
aria-hidden={!showFloatingButton}
tabIndex={showFloatingButton ? undefined : -1}
>
{renderContent()}
</button>
</>
);
};
export default BackButton;