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/content.js CHANGED
@@ -47,10 +47,111 @@ 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/hooks.ts
52
121
  var import_react = require("react");
53
122
 
123
+ // src/og-fetcher.ts
124
+ var ogCache = /* @__PURE__ */ new Map();
125
+ async function fetchOGData(url) {
126
+ if (ogCache.has(url)) {
127
+ return ogCache.get(url);
128
+ }
129
+ try {
130
+ const apiUrl = `https://api.microlink.io?url=${encodeURIComponent(url)}`;
131
+ const response = await fetch(apiUrl);
132
+ if (!response.ok) {
133
+ throw new Error(`Failed to fetch OG data: ${response.status}`);
134
+ }
135
+ const json = await response.json();
136
+ if (json.status !== "success" || !json.data) {
137
+ return null;
138
+ }
139
+ const { title, description, image, url: canonicalUrl, publisher } = json.data;
140
+ const ogData = {
141
+ title: title || void 0,
142
+ description: description || void 0,
143
+ image: image?.url || void 0,
144
+ url: canonicalUrl || url,
145
+ siteName: publisher || void 0
146
+ };
147
+ ogCache.set(url, ogData);
148
+ return ogData;
149
+ } catch (error) {
150
+ console.warn("[react-sharesheet] Failed to fetch OG data:", error);
151
+ return null;
152
+ }
153
+ }
154
+
54
155
  // src/share-functions.ts
55
156
  function shareToWhatsApp(url, text) {
56
157
  const encoded = encodeURIComponent(`${text}
@@ -72,12 +173,24 @@ function shareToFacebook(url) {
72
173
  openUrl(`https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`);
73
174
  }
74
175
  function openInstagram() {
176
+ const availability = checkPlatformAvailability("instagram");
177
+ if (!availability.available) {
178
+ warnUnavailablePlatform("instagram", availability.reason);
179
+ }
75
180
  window.location.href = "instagram://";
76
181
  }
77
182
  function openTikTok() {
183
+ const availability = checkPlatformAvailability("tiktok");
184
+ if (!availability.available) {
185
+ warnUnavailablePlatform("tiktok", availability.reason);
186
+ }
78
187
  window.location.href = "tiktok://";
79
188
  }
80
189
  function openThreads() {
190
+ const availability = checkPlatformAvailability("threads");
191
+ if (!availability.available) {
192
+ warnUnavailablePlatform("threads", availability.reason);
193
+ }
81
194
  window.location.href = "threads://";
82
195
  }
83
196
  function shareToSnapchat(url) {
@@ -85,6 +198,10 @@ function shareToSnapchat(url) {
85
198
  openUrl(`https://www.snapchat.com/scan?attachmentUrl=${encodedUrl}`);
86
199
  }
87
200
  function shareViaSMS(url, text) {
201
+ const availability = checkPlatformAvailability("sms");
202
+ if (!availability.available) {
203
+ warnUnavailablePlatform("sms", availability.reason);
204
+ }
88
205
  const body = encodeURIComponent(`${text}
89
206
  ${url}`);
90
207
  window.location.href = `sms:?body=${body}`;
@@ -122,6 +239,12 @@ function useShareSheet({
122
239
  const canNativeShare = (0, import_react.useMemo)(() => {
123
240
  return typeof navigator !== "undefined" && "share" in navigator;
124
241
  }, []);
242
+ const isMobile = (0, import_react.useMemo)(() => {
243
+ return isMobileDevice();
244
+ }, []);
245
+ const platformAvailability = (0, import_react.useMemo)(() => {
246
+ return getAllPlatformAvailability();
247
+ }, []);
125
248
  const safeUrl = getSafeUrl(shareUrl);
126
249
  const copyLink = (0, import_react.useCallback)(async () => {
127
250
  if (!safeUrl) return;
@@ -210,6 +333,8 @@ function useShareSheet({
210
333
  copied,
211
334
  downloading,
212
335
  safeUrl,
336
+ isMobile,
337
+ platformAvailability,
213
338
  copyLink,
214
339
  nativeShare,
215
340
  downloadFile,
@@ -227,6 +352,37 @@ function useShareSheet({
227
352
  shareReddit
228
353
  };
229
354
  }
355
+ function useOGData(url) {
356
+ const [ogData, setOgData] = (0, import_react.useState)(null);
357
+ const [loading, setLoading] = (0, import_react.useState)(false);
358
+ const [error, setError] = (0, import_react.useState)(null);
359
+ (0, import_react.useEffect)(() => {
360
+ if (!url) {
361
+ setOgData(null);
362
+ setLoading(false);
363
+ setError(null);
364
+ return;
365
+ }
366
+ let cancelled = false;
367
+ setLoading(true);
368
+ setError(null);
369
+ fetchOGData(url).then((data) => {
370
+ if (!cancelled) {
371
+ setOgData(data);
372
+ setLoading(false);
373
+ }
374
+ }).catch((err) => {
375
+ if (!cancelled) {
376
+ setError(err instanceof Error ? err.message : "Failed to fetch OG data");
377
+ setLoading(false);
378
+ }
379
+ });
380
+ return () => {
381
+ cancelled = true;
382
+ };
383
+ }, [url]);
384
+ return { ogData, loading, error };
385
+ }
230
386
 
231
387
  // src/platforms.tsx
232
388
  var import_lucide_react = require("lucide-react");
@@ -364,32 +520,6 @@ var CSS_VAR_DEFAULTS = CSS_VAR_UI_DEFAULTS;
364
520
  var import_jsx_runtime2 = require("react/jsx-runtime");
365
521
  var DEFAULT_BUTTON_SIZE = 45;
366
522
  var DEFAULT_ICON_SIZE = 22;
367
- var IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "gif", "webp", "svg", "bmp", "ico", "avif"];
368
- var VIDEO_EXTENSIONS = ["mp4", "webm", "mov", "avi", "mkv", "m4v", "ogv"];
369
- var AUDIO_EXTENSIONS = ["mp3", "wav", "ogg", "m4a", "aac", "flac", "wma"];
370
- function detectPreviewType(url) {
371
- try {
372
- const pathname = new URL(url, "http://localhost").pathname;
373
- const ext = pathname.split(".").pop()?.toLowerCase() || "";
374
- if (IMAGE_EXTENSIONS.includes(ext)) return "image";
375
- if (VIDEO_EXTENSIONS.includes(ext)) return "video";
376
- if (AUDIO_EXTENSIONS.includes(ext)) return "audio";
377
- if (url.includes("/api/og") || url.includes("og-image")) return "image";
378
- if (url.includes("youtube.com") || url.includes("vimeo.com")) return "video";
379
- return "link";
380
- } catch {
381
- return "link";
382
- }
383
- }
384
- function getFilenameFromUrl(url) {
385
- try {
386
- const pathname = new URL(url, "http://localhost").pathname;
387
- const filename = pathname.split("/").pop() || "";
388
- return decodeURIComponent(filename);
389
- } catch {
390
- return url;
391
- }
392
- }
393
523
  var defaultClasses = {
394
524
  root: "max-w-md mx-auto",
395
525
  header: "text-center mb-2",
@@ -398,12 +528,8 @@ var defaultClasses = {
398
528
  preview: "flex justify-center mb-4 px-4",
399
529
  previewSkeleton: "rounded-xl overflow-hidden",
400
530
  previewImage: "",
401
- previewVideo: "",
402
- previewFile: "",
403
- previewFileIcon: "",
404
- previewFilename: "truncate",
405
- previewLink: "",
406
- grid: "px-2 py-6 flex flex-row items-center gap-4 gap-y-6 flex-wrap justify-center",
531
+ previewMeta: "",
532
+ grid: "px-2 py-6 flex flex-row items-start gap-4 gap-y-6 flex-wrap justify-center",
407
533
  button: "flex flex-col items-center gap-0 text-xs w-[60px] outline-none cursor-pointer group",
408
534
  buttonIcon: "p-2 rounded-full transition-all flex items-center justify-center group-hover:scale-110 group-active:scale-95 mb-2",
409
535
  buttonLabel: ""
@@ -417,28 +543,10 @@ var shimmerKeyframes = `
417
543
  function cssVar(name, fallback) {
418
544
  return `var(${name}, ${fallback})`;
419
545
  }
420
- function normalizePreview(preview) {
421
- if (!preview) return null;
422
- if (typeof preview === "string") {
423
- const type2 = detectPreviewType(preview);
424
- return {
425
- url: preview,
426
- type: type2,
427
- filename: getFilenameFromUrl(preview)
428
- };
429
- }
430
- const type = preview.type === "auto" || !preview.type ? detectPreviewType(preview.url) : preview.type;
431
- return {
432
- ...preview,
433
- type,
434
- filename: preview.filename || getFilenameFromUrl(preview.url)
435
- };
436
- }
437
546
  function ShareSheetContent({
438
547
  title = "Share",
439
548
  shareUrl,
440
549
  shareText,
441
- preview,
442
550
  downloadUrl,
443
551
  downloadFilename,
444
552
  className,
@@ -453,15 +561,15 @@ function ShareSheetContent({
453
561
  labels = {},
454
562
  icons = {}
455
563
  }) {
456
- const [mediaLoaded, setMediaLoaded] = (0, import_react2.useState)(false);
457
- const [mediaError, setMediaError] = (0, import_react2.useState)(false);
458
- const handleMediaLoad = (0, import_react2.useCallback)(() => {
459
- setMediaLoaded(true);
564
+ const [imageLoaded, setImageLoaded] = (0, import_react2.useState)(false);
565
+ const [imageError, setImageError] = (0, import_react2.useState)(false);
566
+ const { ogData, loading: ogLoading } = useOGData(shareUrl);
567
+ const handleImageLoad = (0, import_react2.useCallback)(() => {
568
+ setImageLoaded(true);
460
569
  }, []);
461
- const handleMediaError = (0, import_react2.useCallback)(() => {
462
- setMediaError(true);
570
+ const handleImageError = (0, import_react2.useCallback)(() => {
571
+ setImageError(true);
463
572
  }, []);
464
- const previewConfig = (0, import_react2.useMemo)(() => normalizePreview(preview), [preview]);
465
573
  const shareSheet = useShareSheet({
466
574
  shareUrl,
467
575
  shareText,
@@ -497,6 +605,15 @@ function ShareSheetContent({
497
605
  return PLATFORM_IDS.map((id) => {
498
606
  const Icon = PLATFORM_ICONS[id];
499
607
  const defaultLabel = dynamicLabels[id] ?? PLATFORM_LABELS[id];
608
+ const availability = shareSheet.platformAvailability[id];
609
+ let condition = true;
610
+ if (id === "native") {
611
+ condition = shareSheet.canNativeShare;
612
+ } else if (id === "download") {
613
+ condition = !!downloadUrl;
614
+ } else if (!availability.available) {
615
+ condition = false;
616
+ }
500
617
  return {
501
618
  id,
502
619
  label: labels[id] ?? defaultLabel,
@@ -505,11 +622,10 @@ function ShareSheetContent({
505
622
  bgColor: cssVar(PLATFORM_CSS_VARS[id], PLATFORM_COLORS[id].bg),
506
623
  textColor: PLATFORM_COLORS[id].text,
507
624
  onClick: shareActions[id],
508
- // Conditions for showing certain buttons
509
- condition: id === "native" ? shareSheet.canNativeShare : id === "download" ? !!downloadUrl : true
625
+ condition
510
626
  };
511
627
  });
512
- }, [iconSize, labels, icons, dynamicLabels, shareActions, shareSheet.canNativeShare, downloadUrl]);
628
+ }, [iconSize, labels, icons, dynamicLabels, shareActions, shareSheet.canNativeShare, shareSheet.platformAvailability, downloadUrl]);
513
629
  const visibleButtons = (0, import_react2.useMemo)(() => {
514
630
  return buttons.filter((btn) => {
515
631
  if (btn.condition === false) return false;
@@ -518,49 +634,30 @@ function ShareSheetContent({
518
634
  return true;
519
635
  });
520
636
  }, [buttons, show, hide]);
521
- const showPreview = !!previewConfig;
637
+ const bgColor = cssVar(CSS_VARS_UI.previewBg, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.previewBg]);
638
+ const shimmerColor = cssVar(CSS_VARS_UI.previewShimmer, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.previewShimmer]);
639
+ const textColor = cssVar(CSS_VARS_UI.subtitleColor, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.subtitleColor]);
522
640
  const renderPreview = () => {
523
- if (!previewConfig) return null;
524
- const { type, url, filename, alt, poster } = previewConfig;
525
- const bgColor = cssVar(CSS_VARS_UI.previewBg, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.previewBg]);
526
- const shimmerColor = cssVar(CSS_VARS_UI.previewShimmer, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.previewShimmer]);
527
- const textColor = cssVar(CSS_VARS_UI.subtitleColor, CSS_VAR_UI_DEFAULTS[CSS_VARS_UI.subtitleColor]);
528
- const UrlLabel = ({ displayUrl = url }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
529
- "div",
530
- {
531
- className: cn(defaultClasses.previewFilename, classNames.previewFilename),
532
- style: {
533
- color: textColor,
534
- fontSize: "10px",
535
- opacity: 0.5,
536
- textAlign: "center",
537
- marginTop: "6px"
538
- },
539
- children: displayUrl
540
- }
541
- );
542
- const PlaceholderCard = ({
543
- icon: IconComponent,
544
- isLoading = false,
545
- label,
546
- displayUrl
547
- }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
548
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
641
+ const ogImage = ogData?.image;
642
+ const hasImage = ogImage && !imageError;
643
+ if (ogLoading) {
644
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
549
645
  "div",
550
646
  {
551
647
  className: cn(defaultClasses.previewSkeleton, classNames.previewSkeleton),
552
648
  style: {
553
649
  position: "relative",
554
650
  backgroundColor: bgColor,
555
- width: "200px",
556
- height: "120px",
651
+ width: "100%",
652
+ maxWidth: "320px",
653
+ aspectRatio: "1.91 / 1",
557
654
  overflow: "hidden",
558
655
  display: "flex",
559
656
  alignItems: "center",
560
657
  justifyContent: "center"
561
658
  },
562
659
  children: [
563
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", inset: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
660
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", inset: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
564
661
  "div",
565
662
  {
566
663
  style: {
@@ -571,197 +668,125 @@ function ShareSheetContent({
571
668
  }
572
669
  }
573
670
  ) }),
574
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
575
- "div",
576
- {
577
- style: {
578
- display: "flex",
579
- flexDirection: "column",
580
- alignItems: "center",
581
- justifyContent: "center",
582
- gap: "8px"
583
- },
584
- children: [
585
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(IconComponent, { size: 32, style: { color: textColor, opacity: 0.4 } }),
586
- label && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: textColor, fontSize: "11px", opacity: 0.4 }, children: label })
587
- ]
588
- }
589
- )
671
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Link2, { size: 32, style: { color: textColor, opacity: 0.4 } })
590
672
  ]
591
673
  }
592
- ),
593
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UrlLabel, { displayUrl })
594
- ] });
595
- if (mediaError && (type === "image" || type === "video")) {
596
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PlaceholderCard, { icon: import_lucide_react2.Link2, displayUrl: url });
674
+ ) });
597
675
  }
598
- switch (type) {
599
- case "image":
600
- if (!mediaLoaded) {
601
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
602
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
603
- "div",
604
- {
605
- className: cn(defaultClasses.previewSkeleton, classNames.previewSkeleton),
606
- style: {
607
- position: "relative",
608
- backgroundColor: bgColor,
609
- width: "200px",
610
- height: "120px",
611
- overflow: "hidden",
612
- display: "flex",
613
- alignItems: "center",
614
- justifyContent: "center"
615
- },
616
- children: [
617
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", inset: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
618
- "div",
619
- {
620
- style: {
621
- position: "absolute",
622
- inset: 0,
623
- background: `linear-gradient(90deg, transparent, ${shimmerColor}, transparent)`,
624
- animation: "sharesheet-shimmer 1.5s infinite"
625
- }
626
- }
627
- ) }),
628
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Image, { size: 32, style: { color: textColor, opacity: 0.4 } })
629
- ]
630
- }
631
- ),
632
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UrlLabel, {}),
633
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
634
- "img",
635
- {
636
- src: url,
637
- alt: alt || "Preview",
638
- onLoad: handleMediaLoad,
639
- onError: handleMediaError,
640
- style: { display: "none" }
641
- }
642
- )
643
- ] });
644
- }
645
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
646
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
647
- "img",
676
+ if (!ogData || !hasImage) {
677
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
678
+ "div",
679
+ {
680
+ className: cn(defaultClasses.previewSkeleton, classNames.previewSkeleton),
681
+ style: {
682
+ position: "relative",
683
+ backgroundColor: bgColor,
684
+ width: "100%",
685
+ maxWidth: "320px",
686
+ aspectRatio: "1.91 / 1",
687
+ overflow: "hidden",
688
+ display: "flex",
689
+ alignItems: "center",
690
+ justifyContent: "center"
691
+ },
692
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
693
+ "div",
648
694
  {
649
- src: url,
650
- alt: alt || "Preview",
651
- className: cn(defaultClasses.previewImage, classNames.previewImage),
652
695
  style: {
653
- maxWidth: "100%",
654
- maxHeight: "180px",
655
- borderRadius: "12px",
656
- opacity: 1,
657
- transition: "opacity 0.3s ease-in-out"
658
- }
659
- }
660
- ),
661
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UrlLabel, {})
662
- ] });
663
- case "video":
664
- if (!mediaLoaded) {
665
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
666
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
667
- "div",
668
- {
669
- className: cn(defaultClasses.previewSkeleton, classNames.previewSkeleton),
670
- style: {
671
- position: "relative",
672
- backgroundColor: bgColor,
673
- width: "200px",
674
- height: "120px",
675
- overflow: "hidden",
676
- display: "flex",
677
- alignItems: "center",
678
- justifyContent: "center"
679
- },
680
- children: [
681
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", inset: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
682
- "div",
683
- {
684
- style: {
685
- position: "absolute",
686
- inset: 0,
687
- background: `linear-gradient(90deg, transparent, ${shimmerColor}, transparent)`,
688
- animation: "sharesheet-shimmer 1.5s infinite"
689
- }
690
- }
691
- ) }),
692
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Film, { size: 32, style: { color: textColor, opacity: 0.4 } })
693
- ]
694
- }
695
- ),
696
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UrlLabel, {}),
697
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
698
- "video",
699
- {
700
- src: url,
701
- poster,
702
- onLoadedData: handleMediaLoad,
703
- onError: handleMediaError,
704
- style: { display: "none" },
705
- muted: true,
706
- playsInline: true,
707
- preload: "metadata"
708
- }
709
- )
710
- ] });
711
- }
712
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center" }, children: [
713
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { position: "relative", borderRadius: "12px", overflow: "hidden" }, children: [
714
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
715
- "video",
716
- {
717
- src: url,
718
- poster,
719
- className: cn(defaultClasses.previewVideo, classNames.previewVideo),
720
- style: {
721
- maxWidth: "100%",
722
- maxHeight: "180px",
723
- display: "block"
724
- },
725
- muted: true,
726
- playsInline: true,
727
- preload: "metadata"
728
- }
729
- ),
730
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
731
- "div",
732
- {
733
- style: {
734
- position: "absolute",
735
- inset: 0,
736
- display: "flex",
737
- alignItems: "center",
738
- justifyContent: "center",
739
- pointerEvents: "none"
740
- },
741
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
742
- "div",
696
+ display: "flex",
697
+ flexDirection: "column",
698
+ alignItems: "center",
699
+ justifyContent: "center",
700
+ gap: "8px",
701
+ padding: "16px"
702
+ },
703
+ children: [
704
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Link2, { size: 32, style: { color: textColor, opacity: 0.4 } }),
705
+ ogData?.title && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
706
+ "span",
743
707
  {
744
708
  style: {
745
- backgroundColor: "rgba(0, 0, 0, 0.5)",
746
- borderRadius: "50%",
747
- padding: "10px"
709
+ color: textColor,
710
+ fontSize: "12px",
711
+ opacity: 0.6,
712
+ textAlign: "center",
713
+ maxWidth: "280px",
714
+ overflow: "hidden",
715
+ textOverflow: "ellipsis",
716
+ display: "-webkit-box",
717
+ WebkitLineClamp: 2,
718
+ WebkitBoxOrient: "vertical"
748
719
  },
749
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Play, { size: 20, fill: "white", color: "white" })
720
+ children: ogData.title
750
721
  }
751
722
  )
752
- }
753
- )
754
- ] }),
755
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(UrlLabel, {})
756
- ] });
757
- case "audio":
758
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PlaceholderCard, { icon: import_lucide_react2.Music, label: filename || "Audio", displayUrl: url });
759
- case "file":
760
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PlaceholderCard, { icon: import_lucide_react2.FileText, label: filename || "File", displayUrl: url });
761
- case "link":
762
- default:
763
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PlaceholderCard, { icon: import_lucide_react2.Link2, displayUrl: url });
723
+ ]
724
+ }
725
+ )
726
+ }
727
+ ) });
764
728
  }
729
+ if (!imageLoaded) {
730
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", width: "100%" }, children: [
731
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
732
+ "div",
733
+ {
734
+ className: cn(defaultClasses.previewSkeleton, classNames.previewSkeleton),
735
+ style: {
736
+ position: "relative",
737
+ backgroundColor: bgColor,
738
+ width: "100%",
739
+ maxWidth: "320px",
740
+ aspectRatio: "1.91 / 1",
741
+ overflow: "hidden",
742
+ display: "flex",
743
+ alignItems: "center",
744
+ justifyContent: "center"
745
+ },
746
+ children: [
747
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { position: "absolute", inset: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
748
+ "div",
749
+ {
750
+ style: {
751
+ position: "absolute",
752
+ inset: 0,
753
+ background: `linear-gradient(90deg, transparent, ${shimmerColor}, transparent)`,
754
+ animation: "sharesheet-shimmer 1.5s infinite"
755
+ }
756
+ }
757
+ ) }),
758
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Image, { size: 32, style: { color: textColor, opacity: 0.4 } })
759
+ ]
760
+ }
761
+ ),
762
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
763
+ "img",
764
+ {
765
+ src: ogImage,
766
+ alt: ogData.title || "Preview",
767
+ onLoad: handleImageLoad,
768
+ onError: handleImageError,
769
+ style: { display: "none" }
770
+ }
771
+ )
772
+ ] });
773
+ }
774
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
775
+ "img",
776
+ {
777
+ src: ogImage,
778
+ alt: ogData.title || "Preview",
779
+ className: cn(defaultClasses.previewImage, classNames.previewImage),
780
+ style: {
781
+ width: "100%",
782
+ maxWidth: "320px",
783
+ height: "auto",
784
+ borderRadius: "12px",
785
+ opacity: 1,
786
+ transition: "opacity 0.3s ease-in-out"
787
+ }
788
+ }
789
+ ) });
765
790
  };
766
791
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: cn(defaultClasses.root, classNames.root, className), children: [
767
792
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { dangerouslySetInnerHTML: { __html: shimmerKeyframes } }),
@@ -783,7 +808,7 @@ function ShareSheetContent({
783
808
  }
784
809
  )
785
810
  ] }),
786
- showPreview && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cn(defaultClasses.preview, classNames.preview), children: renderPreview() }),
811
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cn(defaultClasses.preview, classNames.preview), children: renderPreview() }),
787
812
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cn(defaultClasses.grid, classNames.grid), children: visibleButtons.map((btn) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
788
813
  "button",
789
814
  {