react-sharesheet 1.0.0 → 1.2.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.
Files changed (45) hide show
  1. package/README.md +84 -102
  2. package/dist/content.d.mts +3 -3
  3. package/dist/content.d.ts +3 -3
  4. package/dist/content.js +296 -271
  5. package/dist/content.js.map +1 -1
  6. package/dist/content.mjs +298 -273
  7. package/dist/content.mjs.map +1 -1
  8. package/dist/drawer.d.mts +3 -3
  9. package/dist/drawer.d.ts +3 -3
  10. package/dist/drawer.js +298 -275
  11. package/dist/drawer.js.map +1 -1
  12. package/dist/drawer.mjs +300 -277
  13. package/dist/drawer.mjs.map +1 -1
  14. package/dist/headless-B7I228Dt.d.mts +98 -0
  15. package/dist/headless-BiSYHizs.d.ts +98 -0
  16. package/dist/headless.d.mts +3 -50
  17. package/dist/headless.d.ts +3 -50
  18. package/dist/headless.js +165 -0
  19. package/dist/headless.js.map +1 -1
  20. package/dist/headless.mjs +163 -1
  21. package/dist/headless.mjs.map +1 -1
  22. package/dist/index.d.mts +2 -2
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.js +339 -277
  25. package/dist/index.js.map +1 -1
  26. package/dist/index.mjs +329 -278
  27. package/dist/index.mjs.map +1 -1
  28. package/dist/{platforms-DU1DVDFq.d.mts → platforms-omqzPfYX.d.mts} +17 -23
  29. package/dist/{platforms-DU1DVDFq.d.ts → platforms-omqzPfYX.d.ts} +17 -23
  30. package/package.json +24 -7
  31. package/src/ShareSheetContent.tsx +157 -311
  32. package/src/ShareSheetDrawer.tsx +2 -4
  33. package/src/__tests__/hooks.test.ts +203 -0
  34. package/src/__tests__/og-fetcher.test.ts +144 -0
  35. package/src/__tests__/platforms.test.ts +148 -0
  36. package/src/__tests__/setup.ts +22 -0
  37. package/src/__tests__/share-functions.test.ts +152 -0
  38. package/src/__tests__/utils.test.ts +64 -0
  39. package/src/headless.ts +4 -1
  40. package/src/hooks.ts +60 -2
  41. package/src/index.ts +20 -4
  42. package/src/og-fetcher.ts +64 -0
  43. package/src/share-functions.ts +25 -1
  44. package/src/types.ts +17 -24
  45. package/src/utils.ts +125 -0
package/dist/drawer.js CHANGED
@@ -47,6 +47,75 @@ function openUrl(url) {
47
47
  function getSafeUrl(shareUrl) {
48
48
  return shareUrl || (typeof window !== "undefined" ? window.location.href : "");
49
49
  }
50
+ function isMobileDevice() {
51
+ if (typeof navigator === "undefined") return false;
52
+ const userAgent = navigator.userAgent || navigator.vendor || "";
53
+ const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i;
54
+ const hasTouch = typeof window !== "undefined" && ("ontouchstart" in window || navigator.maxTouchPoints > 0);
55
+ return mobileRegex.test(userAgent);
56
+ }
57
+ var MOBILE_ONLY_PLATFORMS = [
58
+ "instagram",
59
+ "tiktok",
60
+ "threads",
61
+ "sms"
62
+ ];
63
+ function checkPlatformAvailability(platform) {
64
+ const isMobile = isMobileDevice();
65
+ if (MOBILE_ONLY_PLATFORMS.includes(platform)) {
66
+ if (!isMobile) {
67
+ return {
68
+ available: false,
69
+ reason: `${platform} requires a mobile device with the app installed`
70
+ };
71
+ }
72
+ }
73
+ if (platform === "sms" && !isMobile) {
74
+ return {
75
+ available: false,
76
+ reason: "SMS sharing requires a mobile device"
77
+ };
78
+ }
79
+ if (platform === "native") {
80
+ const canShare = typeof navigator !== "undefined" && "share" in navigator;
81
+ if (!canShare) {
82
+ return {
83
+ available: false,
84
+ reason: "Native share is not supported by this browser"
85
+ };
86
+ }
87
+ }
88
+ return { available: true };
89
+ }
90
+ function getAllPlatformAvailability() {
91
+ const platforms = [
92
+ "native",
93
+ "copy",
94
+ "download",
95
+ "whatsapp",
96
+ "telegram",
97
+ "instagram",
98
+ "facebook",
99
+ "snapchat",
100
+ "sms",
101
+ "email",
102
+ "linkedin",
103
+ "reddit",
104
+ "x",
105
+ "tiktok",
106
+ "threads"
107
+ ];
108
+ const result = {};
109
+ for (const platform of platforms) {
110
+ result[platform] = checkPlatformAvailability(platform);
111
+ }
112
+ return result;
113
+ }
114
+ function warnUnavailablePlatform(platform, reason) {
115
+ console.warn(
116
+ `[react-sharesheet] ${platform} sharing is not available: ${reason}. This share option may not work correctly on this device.`
117
+ );
118
+ }
50
119
 
51
120
  // src/ShareSheetContent.tsx
52
121
  var import_react2 = require("react");
@@ -55,6 +124,38 @@ var import_lucide_react2 = require("lucide-react");
55
124
  // src/hooks.ts
56
125
  var import_react = require("react");
57
126
 
127
+ // src/og-fetcher.ts
128
+ var ogCache = /* @__PURE__ */ new Map();
129
+ async function fetchOGData(url) {
130
+ if (ogCache.has(url)) {
131
+ return ogCache.get(url);
132
+ }
133
+ try {
134
+ const apiUrl = `https://api.microlink.io?url=${encodeURIComponent(url)}`;
135
+ const response = await fetch(apiUrl);
136
+ if (!response.ok) {
137
+ throw new Error(`Failed to fetch OG data: ${response.status}`);
138
+ }
139
+ const json = await response.json();
140
+ if (json.status !== "success" || !json.data) {
141
+ return null;
142
+ }
143
+ const { title, description, image, url: canonicalUrl, publisher } = json.data;
144
+ const ogData = {
145
+ title: title || void 0,
146
+ description: description || void 0,
147
+ image: image?.url || void 0,
148
+ url: canonicalUrl || url,
149
+ siteName: publisher || void 0
150
+ };
151
+ ogCache.set(url, ogData);
152
+ return ogData;
153
+ } catch (error) {
154
+ console.warn("[react-sharesheet] Failed to fetch OG data:", error);
155
+ return null;
156
+ }
157
+ }
158
+
58
159
  // src/share-functions.ts
59
160
  function shareToWhatsApp(url, text) {
60
161
  const encoded = encodeURIComponent(`${text}
@@ -76,12 +177,24 @@ function shareToFacebook(url) {
76
177
  openUrl(`https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`);
77
178
  }
78
179
  function openInstagram() {
180
+ const availability = checkPlatformAvailability("instagram");
181
+ if (!availability.available) {
182
+ warnUnavailablePlatform("instagram", availability.reason);
183
+ }
79
184
  window.location.href = "instagram://";
80
185
  }
81
186
  function openTikTok() {
187
+ const availability = checkPlatformAvailability("tiktok");
188
+ if (!availability.available) {
189
+ warnUnavailablePlatform("tiktok", availability.reason);
190
+ }
82
191
  window.location.href = "tiktok://";
83
192
  }
84
193
  function openThreads() {
194
+ const availability = checkPlatformAvailability("threads");
195
+ if (!availability.available) {
196
+ warnUnavailablePlatform("threads", availability.reason);
197
+ }
85
198
  window.location.href = "threads://";
86
199
  }
87
200
  function shareToSnapchat(url) {
@@ -89,6 +202,10 @@ function shareToSnapchat(url) {
89
202
  openUrl(`https://www.snapchat.com/scan?attachmentUrl=${encodedUrl}`);
90
203
  }
91
204
  function shareViaSMS(url, text) {
205
+ const availability = checkPlatformAvailability("sms");
206
+ if (!availability.available) {
207
+ warnUnavailablePlatform("sms", availability.reason);
208
+ }
92
209
  const body = encodeURIComponent(`${text}
93
210
  ${url}`);
94
211
  window.location.href = `sms:?body=${body}`;
@@ -126,6 +243,12 @@ function useShareSheet({
126
243
  const canNativeShare = (0, import_react.useMemo)(() => {
127
244
  return typeof navigator !== "undefined" && "share" in navigator;
128
245
  }, []);
246
+ const isMobile = (0, import_react.useMemo)(() => {
247
+ return isMobileDevice();
248
+ }, []);
249
+ const platformAvailability = (0, import_react.useMemo)(() => {
250
+ return getAllPlatformAvailability();
251
+ }, []);
129
252
  const safeUrl = getSafeUrl(shareUrl);
130
253
  const copyLink = (0, import_react.useCallback)(async () => {
131
254
  if (!safeUrl) return;
@@ -214,6 +337,8 @@ function useShareSheet({
214
337
  copied,
215
338
  downloading,
216
339
  safeUrl,
340
+ isMobile,
341
+ platformAvailability,
217
342
  copyLink,
218
343
  nativeShare,
219
344
  downloadFile,
@@ -231,6 +356,37 @@ function useShareSheet({
231
356
  shareReddit
232
357
  };
233
358
  }
359
+ function useOGData(url) {
360
+ const [ogData, setOgData] = (0, import_react.useState)(null);
361
+ const [loading, setLoading] = (0, import_react.useState)(false);
362
+ const [error, setError] = (0, import_react.useState)(null);
363
+ (0, import_react.useEffect)(() => {
364
+ if (!url) {
365
+ setOgData(null);
366
+ setLoading(false);
367
+ setError(null);
368
+ return;
369
+ }
370
+ let cancelled = false;
371
+ setLoading(true);
372
+ setError(null);
373
+ fetchOGData(url).then((data) => {
374
+ if (!cancelled) {
375
+ setOgData(data);
376
+ setLoading(false);
377
+ }
378
+ }).catch((err) => {
379
+ if (!cancelled) {
380
+ setError(err instanceof Error ? err.message : "Failed to fetch OG data");
381
+ setLoading(false);
382
+ }
383
+ });
384
+ return () => {
385
+ cancelled = true;
386
+ };
387
+ }, [url]);
388
+ return { ogData, loading, error };
389
+ }
234
390
 
235
391
  // src/platforms.tsx
236
392
  var import_lucide_react = require("lucide-react");
@@ -368,32 +524,6 @@ var CSS_VAR_DEFAULTS = CSS_VAR_UI_DEFAULTS;
368
524
  var import_jsx_runtime2 = require("react/jsx-runtime");
369
525
  var DEFAULT_BUTTON_SIZE = 45;
370
526
  var DEFAULT_ICON_SIZE = 22;
371
- var IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "gif", "webp", "svg", "bmp", "ico", "avif"];
372
- var VIDEO_EXTENSIONS = ["mp4", "webm", "mov", "avi", "mkv", "m4v", "ogv"];
373
- var AUDIO_EXTENSIONS = ["mp3", "wav", "ogg", "m4a", "aac", "flac", "wma"];
374
- function detectPreviewType(url) {
375
- try {
376
- const pathname = new URL(url, "http://localhost").pathname;
377
- const ext = pathname.split(".").pop()?.toLowerCase() || "";
378
- if (IMAGE_EXTENSIONS.includes(ext)) return "image";
379
- if (VIDEO_EXTENSIONS.includes(ext)) return "video";
380
- if (AUDIO_EXTENSIONS.includes(ext)) return "audio";
381
- if (url.includes("/api/og") || url.includes("og-image")) return "image";
382
- if (url.includes("youtube.com") || url.includes("vimeo.com")) return "video";
383
- return "link";
384
- } catch {
385
- return "link";
386
- }
387
- }
388
- function getFilenameFromUrl(url) {
389
- try {
390
- const pathname = new URL(url, "http://localhost").pathname;
391
- const filename = pathname.split("/").pop() || "";
392
- return decodeURIComponent(filename);
393
- } catch {
394
- return url;
395
- }
396
- }
397
527
  var defaultClasses = {
398
528
  root: "max-w-md mx-auto",
399
529
  header: "text-center mb-2",
@@ -402,12 +532,8 @@ var defaultClasses = {
402
532
  preview: "flex justify-center mb-4 px-4",
403
533
  previewSkeleton: "rounded-xl overflow-hidden",
404
534
  previewImage: "",
405
- previewVideo: "",
406
- previewFile: "",
407
- previewFileIcon: "",
408
- previewFilename: "truncate",
409
- previewLink: "",
410
- grid: "px-2 py-6 flex flex-row items-center gap-4 gap-y-6 flex-wrap justify-center",
535
+ previewMeta: "",
536
+ grid: "px-2 py-6 flex flex-row items-start gap-4 gap-y-6 flex-wrap justify-center",
411
537
  button: "flex flex-col items-center gap-0 text-xs w-[60px] outline-none cursor-pointer group",
412
538
  buttonIcon: "p-2 rounded-full transition-all flex items-center justify-center group-hover:scale-110 group-active:scale-95 mb-2",
413
539
  buttonLabel: ""
@@ -421,28 +547,10 @@ var shimmerKeyframes = `
421
547
  function cssVar(name, fallback) {
422
548
  return `var(${name}, ${fallback})`;
423
549
  }
424
- function normalizePreview(preview) {
425
- if (!preview) return null;
426
- if (typeof preview === "string") {
427
- const type2 = detectPreviewType(preview);
428
- return {
429
- url: preview,
430
- type: type2,
431
- filename: getFilenameFromUrl(preview)
432
- };
433
- }
434
- const type = preview.type === "auto" || !preview.type ? detectPreviewType(preview.url) : preview.type;
435
- return {
436
- ...preview,
437
- type,
438
- filename: preview.filename || getFilenameFromUrl(preview.url)
439
- };
440
- }
441
550
  function ShareSheetContent({
442
551
  title = "Share",
443
552
  shareUrl,
444
553
  shareText,
445
- preview,
446
554
  downloadUrl,
447
555
  downloadFilename,
448
556
  className,
@@ -457,15 +565,15 @@ function ShareSheetContent({
457
565
  labels = {},
458
566
  icons = {}
459
567
  }) {
460
- const [mediaLoaded, setMediaLoaded] = (0, import_react2.useState)(false);
461
- const [mediaError, setMediaError] = (0, import_react2.useState)(false);
462
- const handleMediaLoad = (0, import_react2.useCallback)(() => {
463
- setMediaLoaded(true);
568
+ const [imageLoaded, setImageLoaded] = (0, import_react2.useState)(false);
569
+ const [imageError, setImageError] = (0, import_react2.useState)(false);
570
+ const { ogData, loading: ogLoading } = useOGData(shareUrl);
571
+ const handleImageLoad = (0, import_react2.useCallback)(() => {
572
+ setImageLoaded(true);
464
573
  }, []);
465
- const handleMediaError = (0, import_react2.useCallback)(() => {
466
- setMediaError(true);
574
+ const handleImageError = (0, import_react2.useCallback)(() => {
575
+ setImageError(true);
467
576
  }, []);
468
- const previewConfig = (0, import_react2.useMemo)(() => normalizePreview(preview), [preview]);
469
577
  const shareSheet = useShareSheet({
470
578
  shareUrl,
471
579
  shareText,
@@ -501,6 +609,15 @@ function ShareSheetContent({
501
609
  return PLATFORM_IDS.map((id) => {
502
610
  const Icon = PLATFORM_ICONS[id];
503
611
  const defaultLabel = dynamicLabels[id] ?? PLATFORM_LABELS[id];
612
+ const availability = shareSheet.platformAvailability[id];
613
+ let condition = true;
614
+ if (id === "native") {
615
+ condition = shareSheet.canNativeShare;
616
+ } else if (id === "download") {
617
+ condition = !!downloadUrl;
618
+ } else if (!availability.available) {
619
+ condition = false;
620
+ }
504
621
  return {
505
622
  id,
506
623
  label: labels[id] ?? defaultLabel,
@@ -509,11 +626,10 @@ function ShareSheetContent({
509
626
  bgColor: cssVar(PLATFORM_CSS_VARS[id], PLATFORM_COLORS[id].bg),
510
627
  textColor: PLATFORM_COLORS[id].text,
511
628
  onClick: shareActions[id],
512
- // Conditions for showing certain buttons
513
- condition: id === "native" ? shareSheet.canNativeShare : id === "download" ? !!downloadUrl : true
629
+ condition
514
630
  };
515
631
  });
516
- }, [iconSize, labels, icons, dynamicLabels, shareActions, shareSheet.canNativeShare, downloadUrl]);
632
+ }, [iconSize, labels, icons, dynamicLabels, shareActions, shareSheet.canNativeShare, shareSheet.platformAvailability, downloadUrl]);
517
633
  const visibleButtons = (0, import_react2.useMemo)(() => {
518
634
  return buttons.filter((btn) => {
519
635
  if (btn.condition === false) return false;
@@ -522,49 +638,30 @@ function ShareSheetContent({
522
638
  return true;
523
639
  });
524
640
  }, [buttons, show, hide]);
525
- const showPreview = !!previewConfig;
641
+ const bgColor = cssVar(CSS_VARS_UI.previewBg, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.previewBg]);
642
+ const shimmerColor = cssVar(CSS_VARS_UI.previewShimmer, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.previewShimmer]);
643
+ const textColor = cssVar(CSS_VARS_UI.subtitleColor, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.subtitleColor]);
526
644
  const renderPreview = () => {
527
- if (!previewConfig) return null;
528
- const { type, url, filename, alt, poster } = previewConfig;
529
- const bgColor = cssVar(CSS_VARS_UI.previewBg, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.previewBg]);
530
- const shimmerColor = cssVar(CSS_VARS_UI.previewShimmer, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.previewShimmer]);
531
- const textColor = cssVar(CSS_VARS_UI.subtitleColor, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.subtitleColor]);
532
- const UrlLabel = ({ displayUrl = url }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
533
- "div",
534
- {
535
- className: cn(defaultClasses.previewFilename, classNames.previewFilename),
536
- style: {
537
- color: textColor,
538
- fontSize: "10px",
539
- opacity: 0.5,
540
- textAlign: "center",
541
- marginTop: "6px"
542
- },
543
- children: displayUrl
544
- }
545
- );
546
- const PlaceholderCard = ({
547
- icon: IconComponent,
548
- isLoading = false,
549
- label,
550
- displayUrl
551
- }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
552
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
645
+ const ogImage = ogData?.image;
646
+ const hasImage = ogImage && !imageError;
647
+ if (ogLoading) {
648
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
553
649
  "div",
554
650
  {
555
651
  className: cn(defaultClasses.previewSkeleton, classNames.previewSkeleton),
556
652
  style: {
557
653
  position: "relative",
558
654
  backgroundColor: bgColor,
559
- width: "200px",
560
- height: "120px",
655
+ width: "100%",
656
+ maxWidth: "320px",
657
+ aspectRatio: "1.91 / 1",
561
658
  overflow: "hidden",
562
659
  display: "flex",
563
660
  alignItems: "center",
564
661
  justifyContent: "center"
565
662
  },
566
663
  children: [
567
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", inset: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
664
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", inset: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
568
665
  "div",
569
666
  {
570
667
  style: {
@@ -575,197 +672,125 @@ function ShareSheetContent({
575
672
  }
576
673
  }
577
674
  ) }),
578
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
579
- "div",
580
- {
581
- style: {
582
- display: "flex",
583
- flexDirection: "column",
584
- alignItems: "center",
585
- justifyContent: "center",
586
- gap: "8px"
587
- },
588
- children: [
589
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(IconComponent, { size: 32, style: { color: textColor, opacity: 0.4 } }),
590
- label && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: textColor, fontSize: "11px", opacity: 0.4 }, children: label })
591
- ]
592
- }
593
- )
675
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Link2, { size: 32, style: { color: textColor, opacity: 0.4 } })
594
676
  ]
595
677
  }
596
- ),
597
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UrlLabel, { displayUrl })
598
- ] });
599
- if (mediaError && (type === "image" || type === "video")) {
600
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PlaceholderCard, { icon: import_lucide_react2.Link2, displayUrl: url });
678
+ ) });
601
679
  }
602
- switch (type) {
603
- case "image":
604
- if (!mediaLoaded) {
605
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
606
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
607
- "div",
608
- {
609
- className: cn(defaultClasses.previewSkeleton, classNames.previewSkeleton),
610
- style: {
611
- position: "relative",
612
- backgroundColor: bgColor,
613
- width: "200px",
614
- height: "120px",
615
- overflow: "hidden",
616
- display: "flex",
617
- alignItems: "center",
618
- justifyContent: "center"
619
- },
620
- children: [
621
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", inset: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
622
- "div",
623
- {
624
- style: {
625
- position: "absolute",
626
- inset: 0,
627
- background: `linear-gradient(90deg, transparent, ${shimmerColor}, transparent)`,
628
- animation: "sharesheet-shimmer 1.5s infinite"
629
- }
630
- }
631
- ) }),
632
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Image, { size: 32, style: { color: textColor, opacity: 0.4 } })
633
- ]
634
- }
635
- ),
636
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UrlLabel, {}),
637
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
638
- "img",
639
- {
640
- src: url,
641
- alt: alt || "Preview",
642
- onLoad: handleMediaLoad,
643
- onError: handleMediaError,
644
- style: { display: "none" }
645
- }
646
- )
647
- ] });
648
- }
649
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
650
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
651
- "img",
680
+ if (!ogData || !hasImage) {
681
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
682
+ "div",
683
+ {
684
+ className: cn(defaultClasses.previewSkeleton, classNames.previewSkeleton),
685
+ style: {
686
+ position: "relative",
687
+ backgroundColor: bgColor,
688
+ width: "100%",
689
+ maxWidth: "320px",
690
+ aspectRatio: "1.91 / 1",
691
+ overflow: "hidden",
692
+ display: "flex",
693
+ alignItems: "center",
694
+ justifyContent: "center"
695
+ },
696
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
697
+ "div",
652
698
  {
653
- src: url,
654
- alt: alt || "Preview",
655
- className: cn(defaultClasses.previewImage, classNames.previewImage),
656
699
  style: {
657
- maxWidth: "100%",
658
- maxHeight: "180px",
659
- borderRadius: "12px",
660
- opacity: 1,
661
- transition: "opacity 0.3s ease-in-out"
662
- }
663
- }
664
- ),
665
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UrlLabel, {})
666
- ] });
667
- case "video":
668
- if (!mediaLoaded) {
669
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
670
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
671
- "div",
672
- {
673
- className: cn(defaultClasses.previewSkeleton, classNames.previewSkeleton),
674
- style: {
675
- position: "relative",
676
- backgroundColor: bgColor,
677
- width: "200px",
678
- height: "120px",
679
- overflow: "hidden",
680
- display: "flex",
681
- alignItems: "center",
682
- justifyContent: "center"
683
- },
684
- children: [
685
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", inset: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
686
- "div",
687
- {
688
- style: {
689
- position: "absolute",
690
- inset: 0,
691
- background: `linear-gradient(90deg, transparent, ${shimmerColor}, transparent)`,
692
- animation: "sharesheet-shimmer 1.5s infinite"
693
- }
694
- }
695
- ) }),
696
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Film, { size: 32, style: { color: textColor, opacity: 0.4 } })
697
- ]
698
- }
699
- ),
700
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UrlLabel, {}),
701
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
702
- "video",
703
- {
704
- src: url,
705
- poster,
706
- onLoadedData: handleMediaLoad,
707
- onError: handleMediaError,
708
- style: { display: "none" },
709
- muted: true,
710
- playsInline: true,
711
- preload: "metadata"
712
- }
713
- )
714
- ] });
715
- }
716
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
717
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { position: "relative", borderRadius: "12px", overflow: "hidden" }, children: [
718
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
719
- "video",
720
- {
721
- src: url,
722
- poster,
723
- className: cn(defaultClasses.previewVideo, classNames.previewVideo),
724
- style: {
725
- maxWidth: "100%",
726
- maxHeight: "180px",
727
- display: "block"
728
- },
729
- muted: true,
730
- playsInline: true,
731
- preload: "metadata"
732
- }
733
- ),
734
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
735
- "div",
736
- {
737
- style: {
738
- position: "absolute",
739
- inset: 0,
740
- display: "flex",
741
- alignItems: "center",
742
- justifyContent: "center",
743
- pointerEvents: "none"
744
- },
745
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
746
- "div",
700
+ display: "flex",
701
+ flexDirection: "column",
702
+ alignItems: "center",
703
+ justifyContent: "center",
704
+ gap: "8px",
705
+ padding: "16px"
706
+ },
707
+ children: [
708
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Link2, { size: 32, style: { color: textColor, opacity: 0.4 } }),
709
+ ogData?.title && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
710
+ "span",
747
711
  {
748
712
  style: {
749
- backgroundColor: "rgba(0, 0, 0, 0.5)",
750
- borderRadius: "50%",
751
- padding: "10px"
713
+ color: textColor,
714
+ fontSize: "12px",
715
+ opacity: 0.6,
716
+ textAlign: "center",
717
+ maxWidth: "280px",
718
+ overflow: "hidden",
719
+ textOverflow: "ellipsis",
720
+ display: "-webkit-box",
721
+ WebkitLineClamp: 2,
722
+ WebkitBoxOrient: "vertical"
752
723
  },
753
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Play, { size: 20, fill: "white", color: "white" })
724
+ children: ogData.title
754
725
  }
755
726
  )
756
- }
757
- )
758
- ] }),
759
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UrlLabel, {})
760
- ] });
761
- case "audio":
762
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PlaceholderCard, { icon: import_lucide_react2.Music, label: filename || "Audio", displayUrl: url });
763
- case "file":
764
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PlaceholderCard, { icon: import_lucide_react2.FileText, label: filename || "File", displayUrl: url });
765
- case "link":
766
- default:
767
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PlaceholderCard, { icon: import_lucide_react2.Link2, displayUrl: url });
727
+ ]
728
+ }
729
+ )
730
+ }
731
+ ) });
768
732
  }
733
+ if (!imageLoaded) {
734
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", width: "100%" }, children: [
735
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
736
+ "div",
737
+ {
738
+ className: cn(defaultClasses.previewSkeleton, classNames.previewSkeleton),
739
+ style: {
740
+ position: "relative",
741
+ backgroundColor: bgColor,
742
+ width: "100%",
743
+ maxWidth: "320px",
744
+ aspectRatio: "1.91 / 1",
745
+ overflow: "hidden",
746
+ display: "flex",
747
+ alignItems: "center",
748
+ justifyContent: "center"
749
+ },
750
+ children: [
751
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", inset: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
752
+ "div",
753
+ {
754
+ style: {
755
+ position: "absolute",
756
+ inset: 0,
757
+ background: `linear-gradient(90deg, transparent, ${shimmerColor}, transparent)`,
758
+ animation: "sharesheet-shimmer 1.5s infinite"
759
+ }
760
+ }
761
+ ) }),
762
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Image, { size: 32, style: { color: textColor, opacity: 0.4 } })
763
+ ]
764
+ }
765
+ ),
766
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
767
+ "img",
768
+ {
769
+ src: ogImage,
770
+ alt: ogData.title || "Preview",
771
+ onLoad: handleImageLoad,
772
+ onError: handleImageError,
773
+ style: { display: "none" }
774
+ }
775
+ )
776
+ ] });
777
+ }
778
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
779
+ "img",
780
+ {
781
+ src: ogImage,
782
+ alt: ogData.title || "Preview",
783
+ className: cn(defaultClasses.previewImage, classNames.previewImage),
784
+ style: {
785
+ width: "100%",
786
+ maxWidth: "320px",
787
+ height: "auto",
788
+ borderRadius: "12px",
789
+ opacity: 1,
790
+ transition: "opacity 0.3s ease-in-out"
791
+ }
792
+ }
793
+ ) });
769
794
  };
770
795
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: cn(defaultClasses.root, classNames.root, className), children: [
771
796
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { dangerouslySetInnerHTML: { __html: shimmerKeyframes } }),
@@ -787,7 +812,7 @@ function ShareSheetContent({
787
812
  }
788
813
  )
789
814
  ] }),
790
- showPreview && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cn(defaultClasses.preview, classNames.preview), children: renderPreview() }),
815
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cn(defaultClasses.preview, classNames.preview), children: renderPreview() }),
791
816
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cn(defaultClasses.grid, classNames.grid), children: visibleButtons.map((btn) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
792
817
  "button",
793
818
  {
@@ -827,8 +852,8 @@ function ShareSheetContent({
827
852
  var import_jsx_runtime3 = require("react/jsx-runtime");
828
853
  var defaultDrawerClasses = {
829
854
  overlay: "fixed inset-0 z-[70]",
830
- drawer: "flex flex-col rounded-t-[14px] h-[70%] mt-24 fixed bottom-0 left-0 right-0 z-[80] border-t outline-none",
831
- drawerInner: "p-4 rounded-t-[14px] flex-1 overflow-auto",
855
+ drawer: "flex flex-col rounded-t-[14px] max-h-[90%] fixed bottom-0 left-0 right-0 z-[80] border-t outline-none",
856
+ drawerInner: "p-4 pb-8 rounded-t-[14px] overflow-auto",
832
857
  handle: "mx-auto w-12 h-1.5 shrink-0 rounded-full mb-6",
833
858
  trigger: ""
834
859
  };
@@ -839,7 +864,6 @@ function ShareSheetDrawer({
839
864
  title = "Share",
840
865
  shareUrl,
841
866
  shareText,
842
- preview,
843
867
  downloadUrl,
844
868
  downloadFilename,
845
869
  disabled,
@@ -917,7 +941,6 @@ function ShareSheetDrawer({
917
941
  title,
918
942
  shareUrl,
919
943
  shareText,
920
- preview,
921
944
  downloadUrl,
922
945
  downloadFilename,
923
946
  className,