react-sharesheet 1.1.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.
- package/README.md +48 -53
- package/dist/content.d.mts +2 -2
- package/dist/content.d.ts +2 -2
- package/dist/content.js +104 -3
- package/dist/content.js.map +1 -1
- package/dist/content.mjs +104 -3
- package/dist/content.mjs.map +1 -1
- package/dist/drawer.d.mts +2 -2
- package/dist/drawer.d.ts +2 -2
- package/dist/drawer.js +104 -3
- package/dist/drawer.js.map +1 -1
- package/dist/drawer.mjs +104 -3
- package/dist/drawer.mjs.map +1 -1
- package/dist/headless-B7I228Dt.d.mts +98 -0
- package/dist/headless-BiSYHizs.d.ts +98 -0
- package/dist/headless.d.mts +3 -69
- package/dist/headless.d.ts +3 -69
- package/dist/headless.js +93 -0
- package/dist/headless.js.map +1 -1
- package/dist/headless.mjs +93 -0
- package/dist/headless.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +136 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +127 -4
- package/dist/index.mjs.map +1 -1
- package/dist/{platforms-CDJmSY8E.d.mts → platforms-omqzPfYX.d.mts} +16 -5
- package/dist/{platforms-CDJmSY8E.d.ts → platforms-omqzPfYX.d.ts} +16 -5
- package/package.json +14 -6
- package/src/ShareSheetContent.tsx +14 -5
- package/src/__tests__/hooks.test.ts +4 -14
- package/src/__tests__/share-functions.test.ts +26 -29
- package/src/hooks.ts +11 -1
- package/src/index.ts +16 -1
- package/src/share-functions.ts +25 -1
- package/src/types.ts +16 -4
- package/src/utils.ts +125 -0
package/README.md
CHANGED
|
@@ -3,22 +3,24 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/react-sharesheet)
|
|
4
4
|
[](https://www.npmjs.com/package/react-sharesheet)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
|
-
[](https://
|
|
6
|
+
[](https://sharesheet.gwendall.com)
|
|
7
7
|
|
|
8
|
-
A
|
|
8
|
+
A mobile-first share sheet for React with native share support, built-in Open Graph previews, and 15+ social platforms.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Designed for modern React apps, react-sharesheet ships with a beautiful Tailwind-based drawer UI out of the box, while also exposing fully headless APIs for complete customization.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**[→ Live Demo](https://sharesheet.gwendall.com)**
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
- 🧩 **Headless mode** — Use the hook to build your own UI
|
|
16
|
-
- 📱 **Mobile-first** — Beautiful drawer with native share API support
|
|
17
|
-
- 🔧 **Customizable** — Hide/show platforms, custom labels & icons
|
|
18
|
-
- 📦 **Tree-shakeable** — Import only what you need
|
|
19
|
-
- 🌐 **15+ platforms** — WhatsApp, X, Telegram, Instagram, and more
|
|
14
|
+
## Why react-sharesheet?
|
|
20
15
|
|
|
21
|
-
|
|
16
|
+
- 📱 **Mobile-first drawer UI** - feels native on iOS & Android
|
|
17
|
+
- 🔗 **Native Web Share API** - fallback handled automatically
|
|
18
|
+
- 🖼 **Built-in Open Graph previews** - no extra setup
|
|
19
|
+
- 🧠 **Headless APIs** - build your own UI if needed
|
|
20
|
+
- 🎨 **Themeable** - CSS variables + Tailwind class overrides
|
|
21
|
+
- 🌍 **15+ social platforms** - WhatsApp, X, Telegram, Instagram, and more
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
22
24
|
|
|
23
25
|
```bash
|
|
24
26
|
npm install react-sharesheet
|
|
@@ -43,13 +45,13 @@ Add the package to your `tailwind.config.js` content array:
|
|
|
43
45
|
module.exports = {
|
|
44
46
|
content: [
|
|
45
47
|
"./src/**/*.{js,ts,jsx,tsx}",
|
|
46
|
-
"./node_modules/react-sharesheet/dist/**/*.{js,mjs}", //
|
|
48
|
+
"./node_modules/react-sharesheet/dist/**/*.{js,mjs}", // <-- Add this
|
|
47
49
|
],
|
|
48
50
|
// ...
|
|
49
51
|
}
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
##
|
|
54
|
+
## Quick Start
|
|
53
55
|
|
|
54
56
|
### Full Drawer (recommended)
|
|
55
57
|
|
|
@@ -115,9 +117,9 @@ function CustomShareUI() {
|
|
|
115
117
|
}
|
|
116
118
|
```
|
|
117
119
|
|
|
118
|
-
##
|
|
120
|
+
## Automatic Link Preview
|
|
119
121
|
|
|
120
|
-
The share sheet automatically fetches Open Graph (OG) metadata from the `shareUrl` and displays a rich preview
|
|
122
|
+
The share sheet automatically fetches Open Graph (OG) metadata from the `shareUrl` and displays a rich preview - just like Twitter, Telegram, and other platforms do when you paste a link.
|
|
121
123
|
|
|
122
124
|
```tsx
|
|
123
125
|
<ShareSheetDrawer
|
|
@@ -168,7 +170,7 @@ interface OGData {
|
|
|
168
170
|
}
|
|
169
171
|
```
|
|
170
172
|
|
|
171
|
-
##
|
|
173
|
+
## Theming
|
|
172
174
|
|
|
173
175
|
### CSS Variables
|
|
174
176
|
|
|
@@ -275,7 +277,7 @@ Override any part of the component with `classNames`:
|
|
|
275
277
|
>
|
|
276
278
|
```
|
|
277
279
|
|
|
278
|
-
##
|
|
280
|
+
## API Reference
|
|
279
281
|
|
|
280
282
|
### Props
|
|
281
283
|
|
|
@@ -286,19 +288,19 @@ Override any part of the component with `classNames`:
|
|
|
286
288
|
| `title` | `string` | `"Share"` | Title displayed at the top |
|
|
287
289
|
| `shareUrl` | `string` | **required** | URL to share (OG preview fetched automatically) |
|
|
288
290
|
| `shareText` | `string` | **required** | Text to share |
|
|
289
|
-
| `downloadUrl` | `string` |
|
|
290
|
-
| `downloadFilename` | `string` |
|
|
291
|
-
| `className` | `string` |
|
|
292
|
-
| `classNames` | `object` |
|
|
291
|
+
| `downloadUrl` | `string` | - | URL for download button |
|
|
292
|
+
| `downloadFilename` | `string` | - | Filename for download |
|
|
293
|
+
| `className` | `string` | - | Class for root container |
|
|
294
|
+
| `classNames` | `object` | - | Override sub-component classes |
|
|
293
295
|
| `buttonSize` | `number` | `45` | Button size in pixels |
|
|
294
296
|
| `iconSize` | `number` | `22` | Icon size in pixels |
|
|
295
|
-
| `show` | `ShareOption[]` |
|
|
296
|
-
| `hide` | `ShareOption[]` |
|
|
297
|
-
| `labels` | `object` |
|
|
298
|
-
| `icons` | `object` |
|
|
299
|
-
| `onNativeShare` | `() => void` |
|
|
300
|
-
| `onCopy` | `() => void` |
|
|
301
|
-
| `onDownload` | `() => void` |
|
|
297
|
+
| `show` | `ShareOption[]` | - | Only show these platforms |
|
|
298
|
+
| `hide` | `ShareOption[]` | - | Hide these platforms |
|
|
299
|
+
| `labels` | `object` | - | Custom button labels |
|
|
300
|
+
| `icons` | `object` | - | Custom button icons |
|
|
301
|
+
| `onNativeShare` | `() => void` | - | Native share callback |
|
|
302
|
+
| `onCopy` | `() => void` | - | Copy callback |
|
|
303
|
+
| `onDownload` | `() => void` | - | Download callback |
|
|
302
304
|
|
|
303
305
|
#### ShareSheetDrawer (additional)
|
|
304
306
|
|
|
@@ -306,8 +308,8 @@ Override any part of the component with `classNames`:
|
|
|
306
308
|
|------|------|---------|-------------|
|
|
307
309
|
| `children` | `ReactNode` | **required** | Trigger element |
|
|
308
310
|
| `disabled` | `boolean` | `false` | Disable the trigger |
|
|
309
|
-
| `open` | `boolean` |
|
|
310
|
-
| `onOpenChange` | `(open: boolean) => void` |
|
|
311
|
+
| `open` | `boolean` | - | Controlled open state |
|
|
312
|
+
| `onOpenChange` | `(open: boolean) => void` | - | Open state callback |
|
|
311
313
|
|
|
312
314
|
### ShareOption
|
|
313
315
|
|
|
@@ -371,7 +373,7 @@ const {
|
|
|
371
373
|
});
|
|
372
374
|
```
|
|
373
375
|
|
|
374
|
-
##
|
|
376
|
+
## Exports
|
|
375
377
|
|
|
376
378
|
```ts
|
|
377
379
|
// Everything
|
|
@@ -412,7 +414,7 @@ import {
|
|
|
412
414
|
} from "react-sharesheet/headless";
|
|
413
415
|
```
|
|
414
416
|
|
|
415
|
-
##
|
|
417
|
+
## Platform Utilities
|
|
416
418
|
|
|
417
419
|
Access platform colors, icons, and labels for custom UIs:
|
|
418
420
|
|
|
@@ -465,7 +467,7 @@ function WhatsAppButton({ url, text }: { url: string; text: string }) {
|
|
|
465
467
|
}
|
|
466
468
|
```
|
|
467
469
|
|
|
468
|
-
##
|
|
470
|
+
## Examples
|
|
469
471
|
|
|
470
472
|
### Filter platforms
|
|
471
473
|
|
|
@@ -534,35 +536,28 @@ function ControlledExample() {
|
|
|
534
536
|
}
|
|
535
537
|
```
|
|
536
538
|
|
|
537
|
-
##
|
|
538
|
-
|
|
539
|
-
- React 18+
|
|
540
|
-
- Tailwind CSS (for default styling)
|
|
539
|
+
## Styling & Tailwind
|
|
541
540
|
|
|
542
|
-
|
|
541
|
+
react-sharesheet ships with a Tailwind-based UI by default.
|
|
543
542
|
|
|
544
|
-
|
|
543
|
+
Tailwind is **only required if you use the prebuilt components**.
|
|
544
|
+
If you don't use Tailwind, you can:
|
|
545
545
|
|
|
546
|
-
|
|
546
|
+
- Use the **headless hook** to build your own UI
|
|
547
|
+
- Override all styles via `classNames`
|
|
548
|
+
- Use the exposed CSS variables
|
|
547
549
|
|
|
548
|
-
|
|
549
|
-
// Old imports
|
|
550
|
-
import { ShareMenuDrawer, ShareMenuContent, useShareMenu } from "@gwendall/share-menu";
|
|
550
|
+
This makes react-sharesheet easy to integrate into any React stack.
|
|
551
551
|
|
|
552
|
-
|
|
553
|
-
import { ShareSheetDrawer, ShareSheetContent, useShareSheet } from "react-sharesheet";
|
|
552
|
+
## Requirements
|
|
554
553
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
// New: --sharesheet-drawer-bg
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
Legacy exports (`ShareMenuDrawer`, `ShareMenuContent`, `useShareMenu`) are still available but deprecated.
|
|
554
|
+
- React 18+
|
|
555
|
+
- Tailwind CSS (only for prebuilt components)
|
|
561
556
|
|
|
562
|
-
##
|
|
557
|
+
## Contributing
|
|
563
558
|
|
|
564
559
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
565
560
|
|
|
566
|
-
##
|
|
561
|
+
## License
|
|
567
562
|
|
|
568
563
|
MIT © [Gwendall](https://github.com/gwendall)
|
package/dist/content.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { S as ShareSheetContentProps } from './platforms-
|
|
3
|
-
export { l as CSS_VARS, C as CSS_VARS_UI, m as CSS_VAR_DEFAULTS, k as CSS_VAR_UI_DEFAULTS,
|
|
2
|
+
import { S as ShareSheetContentProps } from './platforms-omqzPfYX.mjs';
|
|
3
|
+
export { l as CSS_VARS, C as CSS_VARS_UI, m as CSS_VAR_DEFAULTS, k as CSS_VAR_UI_DEFAULTS, p as PLATFORM_COLORS, s as PLATFORM_CSS_VARS, f as ShareMenuContentClassNames, d as ShareMenuContentProps, h as ShareOption, b as ShareSheetContentClassNames } from './platforms-omqzPfYX.mjs';
|
|
4
4
|
import 'react';
|
|
5
5
|
|
|
6
6
|
declare function ShareSheetContent({ title, shareUrl, shareText, downloadUrl, downloadFilename, className, classNames, buttonSize, iconSize, onNativeShare, onCopy, onDownload, hide, show, labels, icons, }: ShareSheetContentProps): react_jsx_runtime.JSX.Element;
|
package/dist/content.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { S as ShareSheetContentProps } from './platforms-
|
|
3
|
-
export { l as CSS_VARS, C as CSS_VARS_UI, m as CSS_VAR_DEFAULTS, k as CSS_VAR_UI_DEFAULTS,
|
|
2
|
+
import { S as ShareSheetContentProps } from './platforms-omqzPfYX.js';
|
|
3
|
+
export { l as CSS_VARS, C as CSS_VARS_UI, m as CSS_VAR_DEFAULTS, k as CSS_VAR_UI_DEFAULTS, p as PLATFORM_COLORS, s as PLATFORM_CSS_VARS, f as ShareMenuContentClassNames, d as ShareMenuContentProps, h as ShareOption, b as ShareSheetContentClassNames } from './platforms-omqzPfYX.js';
|
|
4
4
|
import 'react';
|
|
5
5
|
|
|
6
6
|
declare function ShareSheetContent({ title, shareUrl, shareText, downloadUrl, downloadFilename, className, classNames, buttonSize, iconSize, onNativeShare, onCopy, onDownload, hide, show, labels, icons, }: ShareSheetContentProps): react_jsx_runtime.JSX.Element;
|
package/dist/content.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/hooks.ts
|
|
52
121
|
var import_react = require("react");
|
|
@@ -104,12 +173,24 @@ function shareToFacebook(url) {
|
|
|
104
173
|
openUrl(`https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`);
|
|
105
174
|
}
|
|
106
175
|
function openInstagram() {
|
|
176
|
+
const availability = checkPlatformAvailability("instagram");
|
|
177
|
+
if (!availability.available) {
|
|
178
|
+
warnUnavailablePlatform("instagram", availability.reason);
|
|
179
|
+
}
|
|
107
180
|
window.location.href = "instagram://";
|
|
108
181
|
}
|
|
109
182
|
function openTikTok() {
|
|
183
|
+
const availability = checkPlatformAvailability("tiktok");
|
|
184
|
+
if (!availability.available) {
|
|
185
|
+
warnUnavailablePlatform("tiktok", availability.reason);
|
|
186
|
+
}
|
|
110
187
|
window.location.href = "tiktok://";
|
|
111
188
|
}
|
|
112
189
|
function openThreads() {
|
|
190
|
+
const availability = checkPlatformAvailability("threads");
|
|
191
|
+
if (!availability.available) {
|
|
192
|
+
warnUnavailablePlatform("threads", availability.reason);
|
|
193
|
+
}
|
|
113
194
|
window.location.href = "threads://";
|
|
114
195
|
}
|
|
115
196
|
function shareToSnapchat(url) {
|
|
@@ -117,6 +198,10 @@ function shareToSnapchat(url) {
|
|
|
117
198
|
openUrl(`https://www.snapchat.com/scan?attachmentUrl=${encodedUrl}`);
|
|
118
199
|
}
|
|
119
200
|
function shareViaSMS(url, text) {
|
|
201
|
+
const availability = checkPlatformAvailability("sms");
|
|
202
|
+
if (!availability.available) {
|
|
203
|
+
warnUnavailablePlatform("sms", availability.reason);
|
|
204
|
+
}
|
|
120
205
|
const body = encodeURIComponent(`${text}
|
|
121
206
|
${url}`);
|
|
122
207
|
window.location.href = `sms:?body=${body}`;
|
|
@@ -154,6 +239,12 @@ function useShareSheet({
|
|
|
154
239
|
const canNativeShare = (0, import_react.useMemo)(() => {
|
|
155
240
|
return typeof navigator !== "undefined" && "share" in navigator;
|
|
156
241
|
}, []);
|
|
242
|
+
const isMobile = (0, import_react.useMemo)(() => {
|
|
243
|
+
return isMobileDevice();
|
|
244
|
+
}, []);
|
|
245
|
+
const platformAvailability = (0, import_react.useMemo)(() => {
|
|
246
|
+
return getAllPlatformAvailability();
|
|
247
|
+
}, []);
|
|
157
248
|
const safeUrl = getSafeUrl(shareUrl);
|
|
158
249
|
const copyLink = (0, import_react.useCallback)(async () => {
|
|
159
250
|
if (!safeUrl) return;
|
|
@@ -242,6 +333,8 @@ function useShareSheet({
|
|
|
242
333
|
copied,
|
|
243
334
|
downloading,
|
|
244
335
|
safeUrl,
|
|
336
|
+
isMobile,
|
|
337
|
+
platformAvailability,
|
|
245
338
|
copyLink,
|
|
246
339
|
nativeShare,
|
|
247
340
|
downloadFile,
|
|
@@ -512,6 +605,15 @@ function ShareSheetContent({
|
|
|
512
605
|
return PLATFORM_IDS.map((id) => {
|
|
513
606
|
const Icon = PLATFORM_ICONS[id];
|
|
514
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
|
+
}
|
|
515
617
|
return {
|
|
516
618
|
id,
|
|
517
619
|
label: labels[id] ?? defaultLabel,
|
|
@@ -520,11 +622,10 @@ function ShareSheetContent({
|
|
|
520
622
|
bgColor: cssVar(PLATFORM_CSS_VARS[id], PLATFORM_COLORS[id].bg),
|
|
521
623
|
textColor: PLATFORM_COLORS[id].text,
|
|
522
624
|
onClick: shareActions[id],
|
|
523
|
-
|
|
524
|
-
condition: id === "native" ? shareSheet.canNativeShare : id === "download" ? !!downloadUrl : true
|
|
625
|
+
condition
|
|
525
626
|
};
|
|
526
627
|
});
|
|
527
|
-
}, [iconSize, labels, icons, dynamicLabels, shareActions, shareSheet.canNativeShare, downloadUrl]);
|
|
628
|
+
}, [iconSize, labels, icons, dynamicLabels, shareActions, shareSheet.canNativeShare, shareSheet.platformAvailability, downloadUrl]);
|
|
528
629
|
const visibleButtons = (0, import_react2.useMemo)(() => {
|
|
529
630
|
return buttons.filter((btn) => {
|
|
530
631
|
if (btn.condition === false) return false;
|