Files
portifolio-evandrodevbr/app/components/ContactOfferLink.tsx
T

85 lines
3.0 KiB
TypeScript

"use client";
import type { MouseEvent, ReactNode } from "react";
import { prefersReducedMotion } from "@/lib/device";
import {
elementForHash,
focusFragment,
isPlainPrimaryClick,
} from "./cinematic/fragment-nav";
import {
CONTACT_FORM_HASH,
CONTACT_KIND_EVENT,
CONTACT_KIND_PARAM,
type ContactKind,
contactKindHref,
} from "./contact-subject";
import HoverPrefetchLink from "./HoverPrefetchLink";
/**
* Deixa o endereço da home com o fragmento do cartão do formulário (`/#contact-form`) e sem o `?tipo=` que uma oferta
* anterior ou a chegada de outra página deixou (o tipo já está no formulário). `replaceState`, e não uma navegação do
* Next: nada recarrega, e as âncoras seguintes (cabeçalho, rodapé) continuam a rolar a página.
*/
function showContactInAddress() {
const url = new URL(window.location.href);
url.searchParams.delete(CONTACT_KIND_PARAM);
url.hash = CONTACT_FORM_HASH;
const next = `${url.pathname}${url.search}${url.hash}`;
const current = `${window.location.pathname}${window.location.search}${window.location.hash}`;
if (next !== current)
window.history.replaceState(window.history.state, "", next);
}
/**
* Botão de uma oferta da home: leva ao cartão do formulário de contato (`#contact-form`, com o campo do tipo de projeto
* à vista, também no celular) com o tipo já escolhido. Na própria home o tipo segue por evento e a página rola até o
* cartão (sem navegar pelo Next); o `href` com `?tipo=` cobre quem chega de outra página ou abre o link em outra aba.
*
* O normal é o gerenciador de âncoras (fragment-nav + useCinematicScroll) tratar o clique antes deste código, do
* mesmo jeito que trata o link do cabeçalho: rola (com movimento reduzido, em salto), dá o foco ao formulário e
* cancela o clique. Quando ele não o trata (ainda não montou, por exemplo), este botão cancela a navegação, rola e dá
* o foco por conta própria. Em outra página, nada é cancelado: o Link navega para `/?tipo=…#contact-form`.
*/
export default function ContactOfferLink({
kind,
className,
children,
}: {
kind: ContactKind;
className?: string;
children: ReactNode;
}) {
const onClick = (event: MouseEvent<HTMLAnchorElement>) => {
window.dispatchEvent(
new CustomEvent<ContactKind>(CONTACT_KIND_EVENT, { detail: kind }),
);
if (window.location.pathname !== "/") return;
const form = elementForHash(CONTACT_FORM_HASH);
if (!form) return;
if (event.defaultPrevented) {
showContactInAddress();
return;
}
// aba nova, clique com tecla modificadora ou botão do meio: o navegador cuida, com o `?tipo=` do href
if (!isPlainPrimaryClick(event.nativeEvent)) return;
event.preventDefault();
showContactInAddress();
form.scrollIntoView({
behavior: prefersReducedMotion() ? "instant" : "smooth",
block: "start",
});
focusFragment(form);
};
return (
<HoverPrefetchLink
href={contactKindHref(kind)}
className={className}
onClick={onClick}
>
{children}
</HoverPrefetchLink>
);
}