1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71import { useState } from "react";
import * as Dialog from "@radix-ui/react-dialog";
import { FaClipboard, FaCheckCircle } from "react-icons/fa";
import { StyledButton } from "./StyledButton";
import styles from "./ShareModal.module.scss";
interface ShareModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
shareUrl: string;
}
export const ShareModal = ({ open, onOpenChange, shareUrl }: ShareModalProps) => {
const [copied, setCopied] = useState(false);
const copyToClipboard = async () => {
try {
await navigator.clipboard.writeText(shareUrl);
setCopied(true);
setTimeout(() => setCopied(false), 2500);
} catch (err) {
console.error("Failed to copy share URL:", err);
}
};
return (
<Dialog.Root open={open} onOpenChange={onOpenChange}>
<Dialog.Portal>
<Dialog.Overlay className={styles.overlay} />
<Dialog.Content className={styles.content}>
<Dialog.Title className={styles.title}>Share Report</Dialog.Title>
<Dialog.Description className={styles.description}>
Anyone with this link can view this accessibility report — no login required.
</Dialog.Description>
<div className={styles["url-row"]}>
<label htmlFor="share-modal-url" className="sr-only">
Shareable link
</label>
<input
id="share-modal-url"
type="text"
readOnly
value={shareUrl}
onFocus={(e) => e.target.select()}
onClick={(e) => (e.target as HTMLInputElement).select()}
/>
<StyledButton
onClick={copyToClipboard}
label={copied ? "Copied!" : "Copy link"}
icon={copied ? <FaCheckCircle /> : <FaClipboard />}
variant={copied ? "green" : "dark"}
/>
</div>
{/* Announced to screen readers when copy succeeds */}
<div role="status" aria-live="polite" aria-atomic="true" className="sr-only">
{copied ? "Link copied to clipboard." : ""}
</div>
<Dialog.Close asChild>
<button className={styles["close-button"]} aria-label="Close share dialog">
×
</button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
};