react-sharesheet 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,45 @@
1
+ // Headless exports - just the hook and utilities, no styled components
2
+
3
+ export { useShareSheet, useShareMenu, type UseShareSheetOptions, type UseShareMenuOptions } from "./hooks";
4
+
5
+ export {
6
+ shareToWhatsApp,
7
+ shareToTelegram,
8
+ shareToX,
9
+ shareToFacebook,
10
+ openInstagram,
11
+ openTikTok,
12
+ openThreads,
13
+ shareToSnapchat,
14
+ shareViaSMS,
15
+ shareViaEmail,
16
+ shareToLinkedIn,
17
+ shareToReddit,
18
+ } from "./share-functions";
19
+
20
+ export { cn, openUrl, getSafeUrl } from "./utils";
21
+
22
+ // Platform configs (colors, icons, labels) - SINGLE SOURCE OF TRUTH
23
+ export {
24
+ PLATFORMS,
25
+ PLATFORM_IDS,
26
+ PLATFORM_COLORS,
27
+ PLATFORM_ICONS,
28
+ PLATFORM_LABELS,
29
+ PLATFORM_CSS_VARS,
30
+ getPlatform,
31
+ getAllPlatforms,
32
+ getPlatformColor,
33
+ getPlatformIcon,
34
+ getPlatformLabel,
35
+ generateCssVarDefaults,
36
+ type PlatformConfig,
37
+ type PlatformColor,
38
+ } from "./platforms";
39
+
40
+ export type {
41
+ UseShareSheetReturn,
42
+ UseShareMenuReturn,
43
+ ShareOption,
44
+ ShareButtonConfig,
45
+ } from "./types";
package/src/hooks.ts ADDED
@@ -0,0 +1,192 @@
1
+ "use client";
2
+
3
+ import { useMemo, useState, useCallback } from "react";
4
+ import { getSafeUrl } from "./utils";
5
+ import {
6
+ shareToWhatsApp,
7
+ shareToTelegram,
8
+ shareToX,
9
+ shareToFacebook,
10
+ openInstagram,
11
+ openTikTok,
12
+ openThreads,
13
+ shareToSnapchat,
14
+ shareViaSMS,
15
+ shareViaEmail,
16
+ shareToLinkedIn,
17
+ shareToReddit,
18
+ } from "./share-functions";
19
+ import type { UseShareSheetReturn } from "./types";
20
+
21
+ export interface UseShareSheetOptions {
22
+ /** URL to share */
23
+ shareUrl: string;
24
+ /** Text to share */
25
+ shareText: string;
26
+ /** Download URL (optional) */
27
+ downloadUrl?: string | null;
28
+ /** Download filename (optional) */
29
+ downloadFilename?: string;
30
+ /** Email subject (optional) */
31
+ emailSubject?: string;
32
+ /** Callback after native share */
33
+ onNativeShare?: () => void;
34
+ /** Callback after copy */
35
+ onCopy?: () => void;
36
+ /** Callback after download starts */
37
+ onDownload?: () => void;
38
+ }
39
+
40
+ /**
41
+ * Headless hook for share sheet functionality.
42
+ * Use this to build your own custom share UI.
43
+ */
44
+ export function useShareSheet({
45
+ shareUrl,
46
+ shareText,
47
+ downloadUrl,
48
+ downloadFilename,
49
+ emailSubject = "Share",
50
+ onNativeShare,
51
+ onCopy,
52
+ onDownload,
53
+ }: UseShareSheetOptions): UseShareSheetReturn {
54
+ const [copied, setCopied] = useState(false);
55
+ const [downloading, setDownloading] = useState(false);
56
+
57
+ const canNativeShare = useMemo(() => {
58
+ return typeof navigator !== "undefined" && "share" in navigator;
59
+ }, []);
60
+
61
+ const safeUrl = getSafeUrl(shareUrl);
62
+
63
+ const copyLink = useCallback(async () => {
64
+ if (!safeUrl) return;
65
+ try {
66
+ await navigator.clipboard.writeText(safeUrl);
67
+ setCopied(true);
68
+ onCopy?.();
69
+ setTimeout(() => setCopied(false), 1200);
70
+ } catch {
71
+ // ignore
72
+ }
73
+ }, [safeUrl, onCopy]);
74
+
75
+ const nativeShare = useCallback(async () => {
76
+ if (!safeUrl) return;
77
+ const nav = navigator as Navigator & {
78
+ share?: (data: ShareData) => Promise<void>;
79
+ };
80
+ if (!("share" in nav) || typeof nav.share !== "function") return;
81
+ try {
82
+ await nav.share({
83
+ title: shareText,
84
+ text: shareText,
85
+ url: safeUrl,
86
+ });
87
+ onNativeShare?.();
88
+ } catch {
89
+ // user canceled or share failed -> ignore
90
+ }
91
+ }, [safeUrl, shareText, onNativeShare]);
92
+
93
+ const downloadFile = useCallback(async () => {
94
+ const url = (downloadUrl ?? "").trim();
95
+ if (!url) return;
96
+ try {
97
+ setDownloading(true);
98
+ onDownload?.();
99
+ const res = await fetch(url);
100
+ if (!res.ok) throw new Error(`Failed to fetch file (${res.status})`);
101
+ const blob = await res.blob();
102
+ const href = URL.createObjectURL(blob);
103
+ const a = document.createElement("a");
104
+ a.href = href;
105
+ a.download = downloadFilename || "download";
106
+ document.body.appendChild(a);
107
+ a.click();
108
+ a.remove();
109
+ URL.revokeObjectURL(href);
110
+ } catch {
111
+ // ignore
112
+ } finally {
113
+ setDownloading(false);
114
+ }
115
+ }, [downloadUrl, downloadFilename, onDownload]);
116
+
117
+ const shareWhatsApp = useCallback(() => {
118
+ shareToWhatsApp(safeUrl, shareText);
119
+ }, [safeUrl, shareText]);
120
+
121
+ const shareTelegram = useCallback(() => {
122
+ shareToTelegram(safeUrl, shareText);
123
+ }, [safeUrl, shareText]);
124
+
125
+ const shareX = useCallback(() => {
126
+ shareToX(safeUrl, shareText);
127
+ }, [safeUrl, shareText]);
128
+
129
+ const shareFacebook = useCallback(() => {
130
+ shareToFacebook(safeUrl);
131
+ }, [safeUrl]);
132
+
133
+ const shareInstagram = useCallback(() => {
134
+ openInstagram();
135
+ }, []);
136
+
137
+ const shareTikTok = useCallback(() => {
138
+ openTikTok();
139
+ }, []);
140
+
141
+ const shareThreads = useCallback(() => {
142
+ openThreads();
143
+ }, []);
144
+
145
+ const shareSnapchat = useCallback(() => {
146
+ shareToSnapchat(safeUrl);
147
+ }, [safeUrl]);
148
+
149
+ const shareSMS = useCallback(() => {
150
+ shareViaSMS(safeUrl, shareText);
151
+ }, [safeUrl, shareText]);
152
+
153
+ const shareEmail = useCallback(() => {
154
+ shareViaEmail(safeUrl, shareText, emailSubject);
155
+ }, [safeUrl, shareText, emailSubject]);
156
+
157
+ const shareLinkedIn = useCallback(() => {
158
+ shareToLinkedIn(safeUrl);
159
+ }, [safeUrl]);
160
+
161
+ const shareReddit = useCallback(() => {
162
+ shareToReddit(safeUrl, shareText);
163
+ }, [safeUrl, shareText]);
164
+
165
+ return {
166
+ canNativeShare,
167
+ copied,
168
+ downloading,
169
+ safeUrl,
170
+ copyLink,
171
+ nativeShare,
172
+ downloadFile,
173
+ shareWhatsApp,
174
+ shareTelegram,
175
+ shareX,
176
+ shareFacebook,
177
+ shareInstagram,
178
+ shareTikTok,
179
+ shareThreads,
180
+ shareSnapchat,
181
+ shareSMS,
182
+ shareEmail,
183
+ shareLinkedIn,
184
+ shareReddit,
185
+ };
186
+ }
187
+
188
+ // Legacy export for backwards compatibility
189
+ /** @deprecated Use useShareSheet instead */
190
+ export const useShareMenu = useShareSheet;
191
+ /** @deprecated Use UseShareSheetOptions instead */
192
+ export type UseShareMenuOptions = UseShareSheetOptions;
package/src/index.ts ADDED
@@ -0,0 +1,68 @@
1
+ // Styled components
2
+ export { ShareSheetContent, ShareMenuContent } from "./ShareSheetContent";
3
+ export { ShareSheetDrawer, ShareMenuDrawer } from "./ShareSheetDrawer";
4
+
5
+ // Headless hook
6
+ export { useShareSheet, useShareMenu, type UseShareSheetOptions, type UseShareMenuOptions } from "./hooks";
7
+
8
+ // Types
9
+ export type {
10
+ ShareSheetContentProps,
11
+ ShareSheetDrawerProps,
12
+ ShareSheetContentClassNames,
13
+ ShareSheetDrawerClassNames,
14
+ // Legacy types (deprecated)
15
+ ShareMenuContentProps,
16
+ ShareMenuDrawerProps,
17
+ ShareMenuContentClassNames,
18
+ ShareMenuDrawerClassNames,
19
+ // Common types
20
+ ShareOption,
21
+ ShareButtonConfig,
22
+ UseShareSheetReturn,
23
+ UseShareMenuReturn,
24
+ PreviewType,
25
+ PreviewConfig,
26
+ } from "./types";
27
+
28
+ // CSS Variables for UI (drawer, title, etc.)
29
+ export { CSS_VARS_UI, CSS_VAR_UI_DEFAULTS } from "./types";
30
+ // Legacy exports (deprecated but kept for backwards compatibility)
31
+ export { CSS_VARS, CSS_VAR_DEFAULTS } from "./types";
32
+
33
+ // Platform configs (colors, icons, labels) - SINGLE SOURCE OF TRUTH
34
+ export {
35
+ PLATFORMS,
36
+ PLATFORM_IDS,
37
+ PLATFORM_COLORS,
38
+ PLATFORM_ICONS,
39
+ PLATFORM_LABELS,
40
+ PLATFORM_CSS_VARS,
41
+ getPlatform,
42
+ getAllPlatforms,
43
+ getPlatformColor,
44
+ getPlatformIcon,
45
+ getPlatformLabel,
46
+ generateCssVarDefaults,
47
+ type PlatformConfig,
48
+ type PlatformColor,
49
+ } from "./platforms";
50
+
51
+ // Utility functions for custom implementations
52
+ export { cn, openUrl, getSafeUrl } from "./utils";
53
+
54
+ // Individual share functions
55
+ export {
56
+ shareToWhatsApp,
57
+ shareToTelegram,
58
+ shareToX,
59
+ shareToFacebook,
60
+ openInstagram,
61
+ openTikTok,
62
+ openThreads,
63
+ shareToSnapchat,
64
+ shareViaSMS,
65
+ shareViaEmail,
66
+ shareToLinkedIn,
67
+ shareToReddit,
68
+ } from "./share-functions";
@@ -0,0 +1,186 @@
1
+ "use client";
2
+
3
+ import type { ReactNode } from "react";
4
+ import {
5
+ Download,
6
+ Link as LinkIcon,
7
+ Mail,
8
+ MessageCircle,
9
+ Send,
10
+ } from "lucide-react";
11
+ import {
12
+ FaFacebookF,
13
+ FaInstagram,
14
+ FaLinkedin,
15
+ FaReddit,
16
+ FaTelegramPlane,
17
+ FaTiktok,
18
+ FaWhatsapp,
19
+ } from "react-icons/fa";
20
+ import { FaXTwitter, FaThreads, FaSnapchat } from "react-icons/fa6";
21
+ import type { ShareOption } from "./types";
22
+
23
+ /** Platform color configuration */
24
+ export interface PlatformColor {
25
+ /** Background color (hex) */
26
+ bg: string;
27
+ /** Text/icon color (hex) */
28
+ text: string;
29
+ }
30
+
31
+ /** Platform configuration */
32
+ export interface PlatformConfig {
33
+ /** Platform identifier */
34
+ id: ShareOption;
35
+ /** Display label */
36
+ label: string;
37
+ /** Colors */
38
+ colors: PlatformColor;
39
+ /** Icon component (accepts size prop) */
40
+ Icon: (props: { size?: number; className?: string }) => ReactNode;
41
+ /** CSS variable name for background */
42
+ cssVar: string;
43
+ }
44
+
45
+ /** All platform IDs in default order */
46
+ export const PLATFORM_IDS: readonly ShareOption[] = [
47
+ "native",
48
+ "copy",
49
+ "download",
50
+ "whatsapp",
51
+ "telegram",
52
+ "instagram",
53
+ "facebook",
54
+ "snapchat",
55
+ "sms",
56
+ "email",
57
+ "linkedin",
58
+ "reddit",
59
+ "x",
60
+ "tiktok",
61
+ "threads",
62
+ ] as const;
63
+
64
+ /** Platform colors - hex values for each platform (SOURCE OF TRUTH) */
65
+ export const PLATFORM_COLORS: Record<ShareOption, PlatformColor> = {
66
+ native: { bg: "#7c3aed", text: "#ffffff" },
67
+ copy: { bg: "#3b82f6", text: "#ffffff" },
68
+ download: { bg: "#ef4444", text: "#ffffff" },
69
+ whatsapp: { bg: "#25D366", text: "#ffffff" },
70
+ telegram: { bg: "#229ED9", text: "#ffffff" },
71
+ instagram: { bg: "#E1306C", text: "#ffffff" },
72
+ facebook: { bg: "#1877F2", text: "#ffffff" },
73
+ snapchat: { bg: "#FFFC00", text: "#000000" },
74
+ sms: { bg: "#22c55e", text: "#ffffff" },
75
+ email: { bg: "#f97316", text: "#ffffff" },
76
+ linkedin: { bg: "#0A66C2", text: "#ffffff" },
77
+ reddit: { bg: "#FF4500", text: "#ffffff" },
78
+ x: { bg: "#000000", text: "#ffffff" },
79
+ tiktok: { bg: "#000000", text: "#ffffff" },
80
+ threads: { bg: "#000000", text: "#ffffff" },
81
+ } as const;
82
+
83
+ /** Platform labels (SOURCE OF TRUTH) */
84
+ export const PLATFORM_LABELS: Record<ShareOption, string> = {
85
+ native: "Share…",
86
+ copy: "Copy",
87
+ download: "Download",
88
+ whatsapp: "WhatsApp",
89
+ telegram: "Telegram",
90
+ instagram: "Instagram",
91
+ facebook: "Facebook",
92
+ snapchat: "Snapchat",
93
+ sms: "SMS",
94
+ email: "Email",
95
+ linkedin: "LinkedIn",
96
+ reddit: "Reddit",
97
+ x: "X",
98
+ tiktok: "TikTok",
99
+ threads: "Threads",
100
+ } as const;
101
+
102
+ /** Platform icons - React components (SOURCE OF TRUTH) */
103
+ export const PLATFORM_ICONS: Record<ShareOption, (props: { size?: number; className?: string }) => ReactNode> = {
104
+ native: ({ size = 22, className }) => <Send size={size} className={className} />,
105
+ copy: ({ size = 22, className }) => <LinkIcon size={size} className={className} />,
106
+ download: ({ size = 22, className }) => <Download size={size} className={className} />,
107
+ whatsapp: ({ size = 22, className }) => <FaWhatsapp size={size} className={className} />,
108
+ telegram: ({ size = 22, className }) => <FaTelegramPlane size={size} className={className} />,
109
+ instagram: ({ size = 22, className }) => <FaInstagram size={size} className={className} />,
110
+ facebook: ({ size = 22, className }) => <FaFacebookF size={size} className={className} />,
111
+ snapchat: ({ size = 22, className }) => <FaSnapchat size={size} className={className} />,
112
+ sms: ({ size = 22, className }) => <MessageCircle size={size} className={className} />,
113
+ email: ({ size = 22, className }) => <Mail size={size} className={className} />,
114
+ linkedin: ({ size = 22, className }) => <FaLinkedin size={size} className={className} />,
115
+ reddit: ({ size = 22, className }) => <FaReddit size={size} className={className} />,
116
+ x: ({ size = 22, className }) => <FaXTwitter size={size} className={className} />,
117
+ tiktok: ({ size = 22, className }) => <FaTiktok size={size} className={className} />,
118
+ threads: ({ size = 22, className }) => <FaThreads size={size} className={className} />,
119
+ } as const;
120
+
121
+ /** CSS variable names for platform backgrounds */
122
+ export const PLATFORM_CSS_VARS: Record<ShareOption, string> = {
123
+ native: "--sharesheet-native-bg",
124
+ copy: "--sharesheet-copy-bg",
125
+ download: "--sharesheet-download-bg",
126
+ whatsapp: "--sharesheet-whatsapp-bg",
127
+ telegram: "--sharesheet-telegram-bg",
128
+ instagram: "--sharesheet-instagram-bg",
129
+ facebook: "--sharesheet-facebook-bg",
130
+ snapchat: "--sharesheet-snapchat-bg",
131
+ sms: "--sharesheet-sms-bg",
132
+ email: "--sharesheet-email-bg",
133
+ linkedin: "--sharesheet-linkedin-bg",
134
+ reddit: "--sharesheet-reddit-bg",
135
+ x: "--sharesheet-x-bg",
136
+ tiktok: "--sharesheet-tiktok-bg",
137
+ threads: "--sharesheet-threads-bg",
138
+ } as const;
139
+
140
+ /** Full platform configurations */
141
+ export const PLATFORMS: Record<ShareOption, PlatformConfig> = Object.fromEntries(
142
+ PLATFORM_IDS.map((id) => [
143
+ id,
144
+ {
145
+ id,
146
+ label: PLATFORM_LABELS[id],
147
+ colors: PLATFORM_COLORS[id],
148
+ Icon: PLATFORM_ICONS[id],
149
+ cssVar: PLATFORM_CSS_VARS[id],
150
+ },
151
+ ])
152
+ ) as Record<ShareOption, PlatformConfig>;
153
+
154
+ /** Get platform config by id */
155
+ export function getPlatform(id: ShareOption): PlatformConfig {
156
+ return PLATFORMS[id];
157
+ }
158
+
159
+ /** Get all platform configs as array */
160
+ export function getAllPlatforms(): PlatformConfig[] {
161
+ return PLATFORM_IDS.map((id) => PLATFORMS[id]);
162
+ }
163
+
164
+ /** Get platform color */
165
+ export function getPlatformColor(id: ShareOption): PlatformColor {
166
+ return PLATFORM_COLORS[id];
167
+ }
168
+
169
+ /** Get platform icon component */
170
+ export function getPlatformIcon(id: ShareOption): (props: { size?: number; className?: string }) => ReactNode {
171
+ return PLATFORM_ICONS[id];
172
+ }
173
+
174
+ /** Get platform label */
175
+ export function getPlatformLabel(id: ShareOption): string {
176
+ return PLATFORM_LABELS[id];
177
+ }
178
+
179
+ /** Generate CSS variable defaults from platform colors */
180
+ export function generateCssVarDefaults(): Record<string, string> {
181
+ const defaults: Record<string, string> = {};
182
+ PLATFORM_IDS.forEach((id) => {
183
+ defaults[PLATFORM_CSS_VARS[id]] = PLATFORM_COLORS[id].bg;
184
+ });
185
+ return defaults;
186
+ }
@@ -0,0 +1,63 @@
1
+ import { openUrl } from "./utils";
2
+
3
+ export function shareToWhatsApp(url: string, text: string) {
4
+ const encoded = encodeURIComponent(`${text}\n${url}`);
5
+ openUrl(`https://api.whatsapp.com/send?text=${encoded}`);
6
+ }
7
+
8
+ export function shareToTelegram(url: string, text: string) {
9
+ const encodedText = encodeURIComponent(text);
10
+ const encodedUrl = encodeURIComponent(url);
11
+ openUrl(`https://t.me/share/url?url=${encodedUrl}&text=${encodedText}`);
12
+ }
13
+
14
+ export function shareToX(url: string, text: string) {
15
+ const encodedText = encodeURIComponent(text);
16
+ const encodedUrl = encodeURIComponent(url);
17
+ openUrl(`https://x.com/intent/tweet?text=${encodedText}&url=${encodedUrl}`);
18
+ }
19
+
20
+ export function shareToFacebook(url: string) {
21
+ const encodedUrl = encodeURIComponent(url);
22
+ openUrl(`https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`);
23
+ }
24
+
25
+ export function openInstagram() {
26
+ window.location.href = "instagram://";
27
+ }
28
+
29
+ export function openTikTok() {
30
+ window.location.href = "tiktok://";
31
+ }
32
+
33
+ export function openThreads() {
34
+ window.location.href = "threads://";
35
+ }
36
+
37
+ export function shareToSnapchat(url: string) {
38
+ const encodedUrl = encodeURIComponent(url);
39
+ openUrl(`https://www.snapchat.com/scan?attachmentUrl=${encodedUrl}`);
40
+ }
41
+
42
+ export function shareViaSMS(url: string, text: string) {
43
+ const body = encodeURIComponent(`${text}\n${url}`);
44
+ window.location.href = `sms:?body=${body}`;
45
+ }
46
+
47
+ export function shareViaEmail(url: string, text: string, subject = "Share") {
48
+ const encodedSubject = encodeURIComponent(subject);
49
+ const body = encodeURIComponent(`${text}\n\n${url}`);
50
+ window.location.href = `mailto:?subject=${encodedSubject}&body=${body}`;
51
+ }
52
+
53
+ export function shareToLinkedIn(url: string) {
54
+ const encodedUrl = encodeURIComponent(url);
55
+ openUrl(`https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`);
56
+ }
57
+
58
+ export function shareToReddit(url: string, text: string) {
59
+ const encodedText = encodeURIComponent(text);
60
+ const encodedUrl = encodeURIComponent(url);
61
+ openUrl(`https://www.reddit.com/submit?url=${encodedUrl}&title=${encodedText}`);
62
+ }
63
+