Files
tkmind_go/documentation/src/components/SocialShare/index.tsx
T
2026-03-25 14:37:08 +00:00

131 lines
3.8 KiB
TypeScript

import React, { useState, useRef, useEffect, useMemo } from 'react';
import { Share2, Link, Check } from 'lucide-react';
import { IconTwitter } from '@site/src/components/icons/twitter';
import { IconLinkedIn } from '@site/src/components/icons/linkedin';
import { IconFacebook } from '@site/src/components/icons/facebook';
import { IconReddit } from '@site/src/components/icons/reddit';
import styles from './styles.module.css';
const TWITTER_VIA = 'goose_oss';
interface SocialShareProps {
url: string;
title: string;
}
const SocialShare: React.FC<SocialShareProps> = ({ url, title }) => {
const [open, setOpen] = useState(false);
const [copied, setCopied] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
const shareOptions = useMemo(() => {
const encodedUrl = encodeURIComponent(url);
const encodedTitle = encodeURIComponent(title);
return [
{
name: 'Twitter / X',
icon: <IconTwitter />,
url: `https://twitter.com/intent/tweet?url=${encodedUrl}&text=${encodedTitle}&via=${TWITTER_VIA}`,
},
{
name: 'LinkedIn',
icon: <IconLinkedIn />,
url: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
},
{
name: 'Facebook',
icon: <IconFacebook />,
url: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
},
{
name: 'Reddit',
icon: <IconReddit />,
url: `https://reddit.com/submit?url=${encodedUrl}&title=${encodedTitle}`,
},
];
}, [url, title]);
// Close menu on outside click or Escape
useEffect(() => {
if (!open) return;
const handleClickOutside = (e: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
setOpen(false);
}
};
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') setOpen(false);
};
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('keydown', handleEscape);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('keydown', handleEscape);
};
}, [open]);
const openShareWindow = (shareUrl: string) => {
window.open(shareUrl, '_blank', 'width=600,height=500,noopener,noreferrer');
setOpen(false);
};
const handleCopyLink = async () => {
try {
await navigator.clipboard.writeText(url);
setCopied(true);
setTimeout(() => {
setCopied(false);
setOpen(false);
}, 1500);
} catch (err) {
console.error('Failed to copy:', err);
}
};
return (
<div className={styles.wrapper} ref={menuRef}>
<button
onClick={() => setOpen((prev) => !prev)}
className={styles.shareButton}
aria-label="Share this post"
aria-expanded={open}
aria-haspopup="true"
title="Share this post"
>
<Share2 size={16} />
<span>Share</span>
</button>
{open && (
<div className={styles.dropdown} role="menu">
{shareOptions.map((option) => (
<button
key={option.name}
className={styles.dropdownItem}
onClick={() => openShareWindow(option.url)}
role="menuitem"
>
{option.icon}
<span>{option.name}</span>
</button>
))}
<div className={styles.divider} />
<button
className={styles.dropdownItem}
onClick={handleCopyLink}
role="menuitem"
>
{copied ? <Check size={16} /> : <Link size={16} />}
<span>{copied ? 'Copied!' : 'Copy link'}</span>
</button>
</div>
)}
</div>
);
};
export default SocialShare;