Something went wrong. Try again.
Monorepo for Tangled forked from tangled.org/core
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148<script lang="ts"> import { MediaQuery } from "svelte/reactivity"; import { prefersReducedMotion } from "svelte/motion"; import Copy from "$icon/copy"; import { CROSSFADE_DURATION, CROSSFADE_EASING, DRAW_DURATION, DRAW_EASING } from "#lib/motion/spring.ts"; import type { SiteLink } from "#lib/components/landing/shell/links.ts";
/* * A link that copies instead (the footer's Contact). With a mouse, hover * or focus shows a chip under it — "Copy email address" — and a click copies `text` and * says so. On touch there's no hover to explain the copy, so it stays a plain link * (mailto:). If the clipboard is refused, the click goes through to the link too. */
let { link, text }: { link: SiteLink; text: string } = $props();
const mouse = new MediaQuery("(hover: hover) and (pointer: fine)", false); const id = $props.id();
let copied = $state(false); let timer: ReturnType<typeof setTimeout> | undefined;
async function onclick(event: MouseEvent) { if (!mouse.current || !navigator.clipboard) return; event.preventDefault(); try { await navigator.clipboard.writeText(text); } catch { location.href = link.href; return; } copied = true; clearTimeout(timer); timer = setTimeout(() => (copied = false), 1800); }
$effect(() => () => clearTimeout(timer));
const draw = $derived(prefersReducedMotion.current ? 0 : DRAW_DURATION);</script>
<span class="cl" class:copied style="--swap-duration: {CROSSFADE_DURATION}ms; --swap-easing: {CROSSFADE_EASING}; --cl-draw: {draw}ms; --cl-draw-ease: {DRAW_EASING}"> <!-- eslint-disable-next-line svelte/no-navigation-without-resolve -- a mailto: link --> <a href={link.href} {onclick} aria-describedby={mouse.current ? id : undefined}>{link.label}</a> <span class="cl-tip" {id} role="tooltip"> <span class="cl-row"> <Copy class="cl-icon swap-layer {copied ? 'swap-icon-out' : ''}" aria-hidden="true" /> <span class={["swap-layer", copied && "swap-label-out"]}>Copy email address</span> </span> <span class="cl-row" aria-hidden="true"> <svg class={["cl-icon swap-layer", !copied && "swap-icon-out"]} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" > <path class="cl-check" pathLength="1" d="M4 12l5 5L20 6" /> </svg> <span class={["swap-layer", !copied && "swap-label-out"]}>Email address copied</span> </span> </span> <span class="l-sr" aria-live="polite">{copied ? "Email address copied" : ""}</span></span>
<style> .cl { position: relative; display: inline-block; }
/* The chip: the surface in a hairline, small, a little under the link. Only where there's a mouse to hover with. */ .cl-tip { position: absolute; top: calc(100% + 12px); left: 50%; z-index: 5; display: none; align-items: center; padding: 8px; border: 1px solid var(--line); border-radius: 8px; background: var(--color-background-default); box-shadow: var(--pill-shadow); color: var(--muted); font-size: var(--text-small); font-weight: 450; line-height: 1.4; letter-spacing: -0.01em; white-space: nowrap; pointer-events: none; opacity: 0; translate: -50% -4px; transition: opacity 0.15s ease, translate 0.2s var(--ease-out), color 0.15s ease; } .cl-row { display: flex; grid-area: 1 / 1; justify-self: center; align-items: center; gap: 4px; } .cl-row > span { text-box: trim-both cap alphabetic; } .cl-row > :global(.cl-icon) { width: 14px; height: 14px; } .cl-check { stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset var(--cl-draw) var(--cl-draw-ease); } .cl.copied .cl-check { stroke-dashoffset: 0; } @media (hover: hover) and (pointer: fine) { .cl-tip { display: grid; } .cl:hover .cl-tip, .cl:has(a:focus-visible) .cl-tip, .cl.copied .cl-tip { opacity: 1; translate: -50% 0; } } .cl.copied .cl-tip { color: var(--text); }</style>