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,538 @@
1
+ "use client";
2
+
3
+ import { useMemo, useState, useCallback } from "react";
4
+ import { Image, FileText, Music, Film, Link2, Play } from "lucide-react";
5
+ import { cn } from "./utils";
6
+ import { useShareSheet } from "./hooks";
7
+ import {
8
+ PLATFORM_IDS,
9
+ PLATFORM_COLORS,
10
+ PLATFORM_LABELS,
11
+ PLATFORM_ICONS,
12
+ PLATFORM_CSS_VARS,
13
+ } from "./platforms";
14
+ import {
15
+ CSS_VARS_UI,
16
+ CSS_VAR_UI_DEFAULTS,
17
+ type ShareSheetContentProps,
18
+ type ShareOption,
19
+ type ShareButtonConfig,
20
+ type PreviewConfig,
21
+ type PreviewType,
22
+ } from "./types";
23
+
24
+ const DEFAULT_BUTTON_SIZE = 45;
25
+ const DEFAULT_ICON_SIZE = 22;
26
+
27
+ // File extension mappings
28
+ const IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "gif", "webp", "svg", "bmp", "ico", "avif"];
29
+ const VIDEO_EXTENSIONS = ["mp4", "webm", "mov", "avi", "mkv", "m4v", "ogv"];
30
+ const AUDIO_EXTENSIONS = ["mp3", "wav", "ogg", "m4a", "aac", "flac", "wma"];
31
+
32
+ // Detect content type from URL
33
+ function detectPreviewType(url: string): PreviewType {
34
+ try {
35
+ const pathname = new URL(url, "http://localhost").pathname;
36
+ const ext = pathname.split(".").pop()?.toLowerCase() || "";
37
+
38
+ if (IMAGE_EXTENSIONS.includes(ext)) return "image";
39
+ if (VIDEO_EXTENSIONS.includes(ext)) return "video";
40
+ if (AUDIO_EXTENSIONS.includes(ext)) return "audio";
41
+
42
+ // Check for common patterns
43
+ if (url.includes("/api/og") || url.includes("og-image")) return "image";
44
+ if (url.includes("youtube.com") || url.includes("vimeo.com")) return "video";
45
+
46
+ return "link";
47
+ } catch {
48
+ return "link";
49
+ }
50
+ }
51
+
52
+ // Get filename from URL
53
+ function getFilenameFromUrl(url: string): string {
54
+ try {
55
+ const pathname = new URL(url, "http://localhost").pathname;
56
+ const filename = pathname.split("/").pop() || "";
57
+ return decodeURIComponent(filename);
58
+ } catch {
59
+ return url;
60
+ }
61
+ }
62
+
63
+ // Default class names
64
+ const defaultClasses = {
65
+ root: "max-w-md mx-auto",
66
+ header: "text-center mb-2",
67
+ title: "text-2xl font-black",
68
+ subtitle: "mt-1 text-sm",
69
+ preview: "flex justify-center mb-4 px-4",
70
+ previewSkeleton: "rounded-xl overflow-hidden",
71
+ previewImage: "",
72
+ previewVideo: "",
73
+ previewFile: "",
74
+ previewFileIcon: "",
75
+ previewFilename: "truncate",
76
+ previewLink: "",
77
+ grid: "px-2 py-6 flex flex-row items-center gap-4 gap-y-6 flex-wrap justify-center",
78
+ button: "flex flex-col items-center gap-0 text-xs w-[60px] outline-none cursor-pointer group",
79
+ buttonIcon: "p-2 rounded-full transition-all flex items-center justify-center group-hover:scale-110 group-active:scale-95 mb-2",
80
+ buttonLabel: "",
81
+ };
82
+
83
+ // Shimmer keyframes as inline style
84
+ const shimmerKeyframes = `
85
+ @keyframes sharesheet-shimmer {
86
+ 0% { transform: translateX(-100%); }
87
+ 100% { transform: translateX(100%); }
88
+ }
89
+ `;
90
+
91
+ // Helper to create var() with fallback
92
+ function cssVar(name: string, fallback: string): string {
93
+ return `var(${name}, ${fallback})`;
94
+ }
95
+
96
+ // Normalize preview prop to PreviewConfig
97
+ function normalizePreview(preview: string | PreviewConfig | null | undefined): PreviewConfig | null {
98
+ if (!preview) return null;
99
+
100
+ if (typeof preview === "string") {
101
+ const type = detectPreviewType(preview);
102
+ return {
103
+ url: preview,
104
+ type,
105
+ filename: getFilenameFromUrl(preview),
106
+ };
107
+ }
108
+
109
+ // It's already a config object
110
+ const type = preview.type === "auto" || !preview.type ? detectPreviewType(preview.url) : preview.type;
111
+
112
+ return {
113
+ ...preview,
114
+ type,
115
+ filename: preview.filename || getFilenameFromUrl(preview.url),
116
+ };
117
+ }
118
+
119
+ export function ShareSheetContent({
120
+ title = "Share",
121
+ shareUrl,
122
+ shareText,
123
+ preview,
124
+ downloadUrl,
125
+ downloadFilename,
126
+ className,
127
+ classNames = {},
128
+ buttonSize = DEFAULT_BUTTON_SIZE,
129
+ iconSize = DEFAULT_ICON_SIZE,
130
+ onNativeShare,
131
+ onCopy,
132
+ onDownload,
133
+ hide = [],
134
+ show,
135
+ labels = {},
136
+ icons = {},
137
+ }: ShareSheetContentProps) {
138
+ const [mediaLoaded, setMediaLoaded] = useState(false);
139
+ const [mediaError, setMediaError] = useState(false);
140
+
141
+ const handleMediaLoad = useCallback(() => {
142
+ setMediaLoaded(true);
143
+ }, []);
144
+
145
+ const handleMediaError = useCallback(() => {
146
+ setMediaError(true);
147
+ }, []);
148
+
149
+ // Normalize preview config
150
+ const previewConfig = useMemo(() => normalizePreview(preview), [preview]);
151
+
152
+ const shareSheet = useShareSheet({
153
+ shareUrl,
154
+ shareText,
155
+ downloadUrl,
156
+ downloadFilename,
157
+ emailSubject: title,
158
+ onNativeShare,
159
+ onCopy,
160
+ onDownload,
161
+ });
162
+
163
+ // Map platform IDs to their share actions
164
+ const shareActions: Record<ShareOption, () => void> = useMemo(() => ({
165
+ native: () => void shareSheet.nativeShare(),
166
+ copy: () => void shareSheet.copyLink(),
167
+ download: () => void shareSheet.downloadFile(),
168
+ whatsapp: shareSheet.shareWhatsApp,
169
+ telegram: shareSheet.shareTelegram,
170
+ instagram: shareSheet.shareInstagram,
171
+ facebook: shareSheet.shareFacebook,
172
+ snapchat: shareSheet.shareSnapchat,
173
+ sms: shareSheet.shareSMS,
174
+ email: shareSheet.shareEmail,
175
+ linkedin: shareSheet.shareLinkedIn,
176
+ reddit: shareSheet.shareReddit,
177
+ x: shareSheet.shareX,
178
+ tiktok: shareSheet.shareTikTok,
179
+ threads: shareSheet.shareThreads,
180
+ }), [shareSheet]);
181
+
182
+ // Dynamic labels that depend on state
183
+ const dynamicLabels: Partial<Record<ShareOption, string>> = useMemo(() => ({
184
+ copy: shareSheet.copied ? "Copied!" : PLATFORM_LABELS.copy,
185
+ download: shareSheet.downloading ? "..." : PLATFORM_LABELS.download,
186
+ }), [shareSheet.copied, shareSheet.downloading]);
187
+
188
+ // Build button configs from platform data
189
+ const buttons: ShareButtonConfig[] = useMemo(() => {
190
+ return PLATFORM_IDS.map((id) => {
191
+ const Icon = PLATFORM_ICONS[id];
192
+ const defaultLabel = dynamicLabels[id] ?? PLATFORM_LABELS[id];
193
+
194
+ return {
195
+ id,
196
+ label: labels[id] ?? defaultLabel,
197
+ icon: icons[id] ?? <Icon size={iconSize} />,
198
+ // Use CSS var with fallback to platform color
199
+ bgColor: cssVar(PLATFORM_CSS_VARS[id], PLATFORM_COLORS[id].bg),
200
+ textColor: PLATFORM_COLORS[id].text,
201
+ onClick: shareActions[id],
202
+ // Conditions for showing certain buttons
203
+ condition: id === "native" ? shareSheet.canNativeShare
204
+ : id === "download" ? !!downloadUrl
205
+ : true,
206
+ };
207
+ });
208
+ }, [iconSize, labels, icons, dynamicLabels, shareActions, shareSheet.canNativeShare, downloadUrl]);
209
+
210
+ const visibleButtons = useMemo(() => {
211
+ return buttons.filter((btn) => {
212
+ // Check condition (e.g., canNativeShare, downloadUrl exists)
213
+ if (btn.condition === false) return false;
214
+ // Filter by show list if provided
215
+ if (show && show.length > 0) return show.includes(btn.id);
216
+ // Filter by hide list
217
+ if (hide.includes(btn.id)) return false;
218
+ return true;
219
+ });
220
+ }, [buttons, show, hide]);
221
+
222
+ const showPreview = !!previewConfig;
223
+
224
+ // Render preview based on type
225
+ const renderPreview = () => {
226
+ if (!previewConfig) return null;
227
+
228
+ const { type, url, filename, alt, poster } = previewConfig;
229
+ const bgColor = cssVar(CSS_VARS_UI.previewBg, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.previewBg]);
230
+ const shimmerColor = cssVar(CSS_VARS_UI.previewShimmer, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.previewShimmer]);
231
+ const textColor = cssVar(CSS_VARS_UI.subtitleColor, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.subtitleColor]);
232
+
233
+ // Floating URL label (centered below)
234
+ const UrlLabel = ({ displayUrl = url }: { displayUrl?: string }) => (
235
+ <div
236
+ className={cn(defaultClasses.previewFilename, classNames.previewFilename)}
237
+ style={{
238
+ color: textColor,
239
+ fontSize: "10px",
240
+ opacity: 0.5,
241
+ textAlign: "center",
242
+ marginTop: "6px",
243
+ }}
244
+ >
245
+ {displayUrl}
246
+ </div>
247
+ );
248
+
249
+ // Placeholder card for non-media types or loading/error states
250
+ const PlaceholderCard = ({
251
+ icon: IconComponent,
252
+ isLoading = false,
253
+ label,
254
+ displayUrl,
255
+ }: {
256
+ icon: typeof Link2;
257
+ isLoading?: boolean;
258
+ label?: string;
259
+ displayUrl?: string;
260
+ }) => (
261
+ <div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
262
+ <div
263
+ className={cn(defaultClasses.previewSkeleton, classNames.previewSkeleton)}
264
+ style={{
265
+ position: "relative",
266
+ backgroundColor: bgColor,
267
+ width: "200px",
268
+ height: "120px",
269
+ overflow: "hidden",
270
+ display: "flex",
271
+ alignItems: "center",
272
+ justifyContent: "center",
273
+ }}
274
+ >
275
+ {isLoading && (
276
+ <div style={{ position: "absolute", inset: 0, overflow: "hidden" }}>
277
+ <div
278
+ style={{
279
+ position: "absolute",
280
+ inset: 0,
281
+ background: `linear-gradient(90deg, transparent, ${shimmerColor}, transparent)`,
282
+ animation: "sharesheet-shimmer 1.5s infinite",
283
+ }}
284
+ />
285
+ </div>
286
+ )}
287
+ <div
288
+ style={{
289
+ display: "flex",
290
+ flexDirection: "column",
291
+ alignItems: "center",
292
+ justifyContent: "center",
293
+ gap: "8px",
294
+ }}
295
+ >
296
+ <IconComponent size={32} style={{ color: textColor, opacity: 0.4 }} />
297
+ {label && (
298
+ <span style={{ color: textColor, fontSize: "11px", opacity: 0.4 }}>
299
+ {label}
300
+ </span>
301
+ )}
302
+ </div>
303
+ </div>
304
+ <UrlLabel displayUrl={displayUrl} />
305
+ </div>
306
+ );
307
+
308
+ // If there was an error loading media, show fallback
309
+ if (mediaError && (type === "image" || type === "video")) {
310
+ return <PlaceholderCard icon={Link2} displayUrl={url} />;
311
+ }
312
+
313
+ switch (type) {
314
+ case "image":
315
+ // Show placeholder while loading, then show image with correct aspect ratio
316
+ if (!mediaLoaded) {
317
+ return (
318
+ <div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
319
+ <div
320
+ className={cn(defaultClasses.previewSkeleton, classNames.previewSkeleton)}
321
+ style={{
322
+ position: "relative",
323
+ backgroundColor: bgColor,
324
+ width: "200px",
325
+ height: "120px",
326
+ overflow: "hidden",
327
+ display: "flex",
328
+ alignItems: "center",
329
+ justifyContent: "center",
330
+ }}
331
+ >
332
+ <div style={{ position: "absolute", inset: 0, overflow: "hidden" }}>
333
+ <div
334
+ style={{
335
+ position: "absolute",
336
+ inset: 0,
337
+ background: `linear-gradient(90deg, transparent, ${shimmerColor}, transparent)`,
338
+ animation: "sharesheet-shimmer 1.5s infinite",
339
+ }}
340
+ />
341
+ </div>
342
+ <Image size={32} style={{ color: textColor, opacity: 0.4 }} />
343
+ </div>
344
+ <UrlLabel />
345
+ {/* Hidden image for preloading */}
346
+ {/* eslint-disable-next-line @next/next/no-img-element */}
347
+ <img
348
+ src={url}
349
+ alt={alt || "Preview"}
350
+ onLoad={handleMediaLoad}
351
+ onError={handleMediaError}
352
+ style={{ display: "none" }}
353
+ />
354
+ </div>
355
+ );
356
+ }
357
+ // Image loaded - show with correct aspect ratio
358
+ return (
359
+ <div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
360
+ {/* eslint-disable-next-line @next/next/no-img-element */}
361
+ <img
362
+ src={url}
363
+ alt={alt || "Preview"}
364
+ className={cn(defaultClasses.previewImage, classNames.previewImage)}
365
+ style={{
366
+ maxWidth: "100%",
367
+ maxHeight: "180px",
368
+ borderRadius: "12px",
369
+ opacity: 1,
370
+ transition: "opacity 0.3s ease-in-out",
371
+ }}
372
+ />
373
+ <UrlLabel />
374
+ </div>
375
+ );
376
+
377
+ case "video":
378
+ if (!mediaLoaded) {
379
+ return (
380
+ <div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
381
+ <div
382
+ className={cn(defaultClasses.previewSkeleton, classNames.previewSkeleton)}
383
+ style={{
384
+ position: "relative",
385
+ backgroundColor: bgColor,
386
+ width: "200px",
387
+ height: "120px",
388
+ overflow: "hidden",
389
+ display: "flex",
390
+ alignItems: "center",
391
+ justifyContent: "center",
392
+ }}
393
+ >
394
+ <div style={{ position: "absolute", inset: 0, overflow: "hidden" }}>
395
+ <div
396
+ style={{
397
+ position: "absolute",
398
+ inset: 0,
399
+ background: `linear-gradient(90deg, transparent, ${shimmerColor}, transparent)`,
400
+ animation: "sharesheet-shimmer 1.5s infinite",
401
+ }}
402
+ />
403
+ </div>
404
+ <Film size={32} style={{ color: textColor, opacity: 0.4 }} />
405
+ </div>
406
+ <UrlLabel />
407
+ {/* Hidden video for preloading */}
408
+ <video
409
+ src={url}
410
+ poster={poster}
411
+ onLoadedData={handleMediaLoad}
412
+ onError={handleMediaError}
413
+ style={{ display: "none" }}
414
+ muted
415
+ playsInline
416
+ preload="metadata"
417
+ />
418
+ </div>
419
+ );
420
+ }
421
+ // Video loaded
422
+ return (
423
+ <div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
424
+ <div style={{ position: "relative", borderRadius: "12px", overflow: "hidden" }}>
425
+ <video
426
+ src={url}
427
+ poster={poster}
428
+ className={cn(defaultClasses.previewVideo, classNames.previewVideo)}
429
+ style={{
430
+ maxWidth: "100%",
431
+ maxHeight: "180px",
432
+ display: "block",
433
+ }}
434
+ muted
435
+ playsInline
436
+ preload="metadata"
437
+ />
438
+ {/* Play icon overlay */}
439
+ <div
440
+ style={{
441
+ position: "absolute",
442
+ inset: 0,
443
+ display: "flex",
444
+ alignItems: "center",
445
+ justifyContent: "center",
446
+ pointerEvents: "none",
447
+ }}
448
+ >
449
+ <div
450
+ style={{
451
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
452
+ borderRadius: "50%",
453
+ padding: "10px",
454
+ }}
455
+ >
456
+ <Play size={20} fill="white" color="white" />
457
+ </div>
458
+ </div>
459
+ </div>
460
+ <UrlLabel />
461
+ </div>
462
+ );
463
+
464
+ case "audio":
465
+ return <PlaceholderCard icon={Music} label={filename || "Audio"} displayUrl={url} />;
466
+
467
+ case "file":
468
+ return <PlaceholderCard icon={FileText} label={filename || "File"} displayUrl={url} />;
469
+
470
+ case "link":
471
+ default:
472
+ return <PlaceholderCard icon={Link2} displayUrl={url} />;
473
+ }
474
+ };
475
+
476
+ return (
477
+ <div className={cn(defaultClasses.root, classNames.root, className)}>
478
+ {/* Inject shimmer keyframes */}
479
+ <style dangerouslySetInnerHTML={{ __html: shimmerKeyframes }} />
480
+
481
+ <div className={cn(defaultClasses.header, classNames.header)}>
482
+ <div
483
+ className={cn(defaultClasses.title, classNames.title)}
484
+ style={{ color: cssVar(CSS_VARS_UI.titleColor, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.titleColor]) }}
485
+ >
486
+ {title}
487
+ </div>
488
+ <div
489
+ className={cn(defaultClasses.subtitle, classNames.subtitle)}
490
+ style={{ color: cssVar(CSS_VARS_UI.subtitleColor, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.subtitleColor]) }}
491
+ >
492
+ {shareText}
493
+ </div>
494
+ </div>
495
+
496
+ {/* Content Preview */}
497
+ {showPreview && (
498
+ <div className={cn(defaultClasses.preview, classNames.preview)}>
499
+ {renderPreview()}
500
+ </div>
501
+ )}
502
+
503
+ <div className={cn(defaultClasses.grid, classNames.grid)}>
504
+ {visibleButtons.map((btn) => (
505
+ <button
506
+ key={btn.id}
507
+ type="button"
508
+ className={cn(defaultClasses.button, classNames.button)}
509
+ onClick={btn.onClick}
510
+ >
511
+ <div
512
+ className={cn(defaultClasses.buttonIcon, classNames.buttonIcon)}
513
+ style={{
514
+ width: buttonSize,
515
+ height: buttonSize,
516
+ backgroundColor: btn.bgColor,
517
+ color: btn.textColor,
518
+ }}
519
+ >
520
+ {btn.icon}
521
+ </div>
522
+ <div
523
+ className={cn(defaultClasses.buttonLabel, classNames.buttonLabel)}
524
+ style={{ color: cssVar(CSS_VARS_UI.buttonLabelColor, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.buttonLabelColor]) }}
525
+ >
526
+ {btn.label}
527
+ </div>
528
+ </button>
529
+ ))}
530
+ </div>
531
+ </div>
532
+ );
533
+ }
534
+
535
+ // Legacy export for backwards compatibility
536
+ /** @deprecated Use ShareSheetContent instead */
537
+ export const ShareMenuContent = ShareSheetContent;
538
+
@@ -0,0 +1,128 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Drawer } from "vaul";
5
+
6
+ import { cn } from "./utils";
7
+ import { ShareSheetContent } from "./ShareSheetContent";
8
+ import { CSS_VARS_UI, CSS_VAR_UI_DEFAULTS, type ShareSheetDrawerProps } from "./types";
9
+
10
+ // Default class names for drawer
11
+ const defaultDrawerClasses = {
12
+ overlay: "fixed inset-0 z-[70]",
13
+ drawer: "flex flex-col rounded-t-[14px] h-[70%] mt-24 fixed bottom-0 left-0 right-0 z-[80] border-t outline-none",
14
+ drawerInner: "p-4 rounded-t-[14px] flex-1 overflow-auto",
15
+ handle: "mx-auto w-12 h-1.5 shrink-0 rounded-full mb-6",
16
+ trigger: "",
17
+ };
18
+
19
+ // Helper to create var() with fallback
20
+ function cssVar(name: string, fallback: string): string {
21
+ return `var(${name}, ${fallback})`;
22
+ }
23
+
24
+ export function ShareSheetDrawer({
25
+ title = "Share",
26
+ shareUrl,
27
+ shareText,
28
+ preview,
29
+ downloadUrl,
30
+ downloadFilename,
31
+ disabled,
32
+ children,
33
+ open: controlledOpen,
34
+ onOpenChange: controlledOnOpenChange,
35
+ className,
36
+ classNames = {},
37
+ buttonSize,
38
+ iconSize,
39
+ onNativeShare,
40
+ onCopy,
41
+ onDownload,
42
+ hide,
43
+ show,
44
+ labels,
45
+ icons,
46
+ }: ShareSheetDrawerProps) {
47
+ const [internalOpen, setInternalOpen] = useState(false);
48
+
49
+ const isControlled = controlledOpen !== undefined;
50
+ const open = isControlled ? controlledOpen : internalOpen;
51
+ const setOpen = isControlled
52
+ ? (value: boolean) => controlledOnOpenChange?.(value)
53
+ : setInternalOpen;
54
+
55
+ return (
56
+ <Drawer.Root open={open} onOpenChange={setOpen} shouldScaleBackground>
57
+ <Drawer.Trigger asChild>
58
+ <div
59
+ className={cn(
60
+ defaultDrawerClasses.trigger,
61
+ classNames.trigger,
62
+ disabled ? "pointer-events-none opacity-50" : ""
63
+ )}
64
+ >
65
+ {children}
66
+ </div>
67
+ </Drawer.Trigger>
68
+ <Drawer.Portal>
69
+ <Drawer.Overlay
70
+ className={cn(defaultDrawerClasses.overlay, classNames.overlay)}
71
+ style={{
72
+ backgroundColor: cssVar(CSS_VARS_UI.overlayBg, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.overlayBg]),
73
+ }}
74
+ />
75
+ <Drawer.Content
76
+ className={cn(defaultDrawerClasses.drawer, classNames.drawer)}
77
+ style={{
78
+ backgroundColor: cssVar(CSS_VARS_UI.drawerBg, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.drawerBg]),
79
+ borderColor: cssVar(CSS_VARS_UI.drawerBorder, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.drawerBorder]),
80
+ }}
81
+ >
82
+ <Drawer.Title className="sr-only">{title}</Drawer.Title>
83
+ <div
84
+ className={cn(defaultDrawerClasses.drawerInner, classNames.drawerInner)}
85
+ style={{
86
+ backgroundColor: cssVar(CSS_VARS_UI.drawerBg, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.drawerBg]),
87
+ }}
88
+ >
89
+ <div
90
+ className={cn(defaultDrawerClasses.handle, classNames.handle)}
91
+ style={{
92
+ backgroundColor: cssVar(CSS_VARS_UI.handleBg, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.handleBg]),
93
+ }}
94
+ />
95
+
96
+ <ShareSheetContent
97
+ title={title}
98
+ shareUrl={shareUrl}
99
+ shareText={shareText}
100
+ preview={preview}
101
+ downloadUrl={downloadUrl}
102
+ downloadFilename={downloadFilename}
103
+ className={className}
104
+ classNames={classNames}
105
+ buttonSize={buttonSize}
106
+ iconSize={iconSize}
107
+ onNativeShare={() => {
108
+ onNativeShare?.();
109
+ setOpen(false);
110
+ }}
111
+ onCopy={onCopy}
112
+ onDownload={onDownload}
113
+ hide={hide}
114
+ show={show}
115
+ labels={labels}
116
+ icons={icons}
117
+ />
118
+ </div>
119
+ </Drawer.Content>
120
+ </Drawer.Portal>
121
+ </Drawer.Root>
122
+ );
123
+ }
124
+
125
+ // Legacy export for backwards compatibility
126
+ /** @deprecated Use ShareSheetDrawer instead */
127
+ export const ShareMenuDrawer = ShareSheetDrawer;
128
+
package/src/content.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { ShareSheetContent, ShareMenuContent } from "./ShareSheetContent";
2
+ export type { ShareSheetContentProps, ShareSheetContentClassNames, ShareMenuContentProps, ShareMenuContentClassNames, ShareOption } from "./types";
3
+ export { CSS_VARS_UI, CSS_VAR_UI_DEFAULTS, CSS_VARS, CSS_VAR_DEFAULTS } from "./types";
4
+ export { PLATFORM_COLORS, PLATFORM_CSS_VARS } from "./platforms";
package/src/drawer.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { ShareSheetDrawer, ShareMenuDrawer } from "./ShareSheetDrawer";
2
+ export type { ShareSheetDrawerProps, ShareSheetDrawerClassNames, ShareMenuDrawerProps, ShareMenuDrawerClassNames, ShareOption } from "./types";
3
+ export { CSS_VARS_UI, CSS_VAR_UI_DEFAULTS, CSS_VARS, CSS_VAR_DEFAULTS } from "./types";
4
+ export { PLATFORM_COLORS, PLATFORM_CSS_VARS } from "./platforms";