@dismissible/react-client 1.0.0-canary.4.a9fb4a5 → 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.
- package/README.md +195 -55
- package/dist/config/api.config.d.ts +14 -0
- package/dist/dismissible-client.es.js +208 -205
- package/dist/dismissible-client.umd.js +1 -1
- package/dist/hooks/useDismissibleItem.d.ts +14 -16
- package/dist/root.d.ts +5 -0
- package/package.json +16 -17
- package/CHANGELOG.md +0 -12
package/README.md
CHANGED
|
@@ -1,37 +1,26 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<a href="https://dismissible.io" target="_blank"><img src="https://raw.githubusercontent.com/DismissibleIo/dismissible-api/main/docs/images/dismissible_logo.png" width="240" alt="Dismissible" /></a>
|
|
3
|
-
</p>
|
|
4
|
-
|
|
5
|
-
<p align="center">Never Show The Same Thing Twice!</p>
|
|
6
|
-
<p align="center">
|
|
7
|
-
<a href="https://www.npmjs.com/package/@dismissible/react-client" target="_blank"><img src="https://img.shields.io/npm/v/@dismissible/react-client.svg" alt="NPM Version" /></a>
|
|
8
|
-
<a href="https://github.com/dismissibleio/dismissible-react-client/blob/main/LICENSE" target="_blank"><img src="https://img.shields.io/npm/l/@dismissible/react-client.svg" alt="Package License" /></a>
|
|
9
|
-
<a href="https://www.npmjs.com/package/@dismissible/react-client" target="_blank"><img src="https://img.shields.io/npm/dm/@dismissible/react-client.svg" alt="NPM Downloads" /></a>
|
|
10
|
-
<a href="https://github.com/dismissibleio/dismissible-react-client" target="_blank"><img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/dismissibleio/dismissible-react-client/publish.yml" /></a>
|
|
11
|
-
<a href="https://paypal.me/joshstuartx" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" /></a>
|
|
12
|
-
</p>
|
|
13
|
-
|
|
14
|
-
Dismissible manages the state of your UI elements across sessions, so your users see what matters, once! No more onboarding messages reappearing on every tab, no more notifications haunting users across devices. Dismissible syncs dismissal state everywhere, so every message is intentional, never repetitive.
|
|
15
|
-
|
|
16
1
|
# @dismissible/react-client
|
|
17
2
|
|
|
18
|
-
|
|
3
|
+
A React component library for creating dismissible UI elements with persistent state management.
|
|
19
4
|
|
|
20
|
-
|
|
5
|
+
**Free and open source** - use with the [Dismissible API Server](https://github.com/DismissibleIo/dismissible-api) that you can self-host with Docker or integrate into your NestJS application.
|
|
21
6
|
|
|
22
|
-
**[dismissible.io](https://dismissible.io)** | **[Documentation](https://dismissible.io/docs)** | **[API Server](https://github.com/DismissibleIo/dismissible-api)**
|
|
7
|
+
🌐 **[dismissible.io](https://dismissible.io)** | 📖 **[Documentation](https://dismissible.io/docs)** | 🐙 **[API Server](https://github.com/DismissibleIo/dismissible-api)**
|
|
8
|
+
|
|
9
|
+
[](https://badge.fury.io/js/@dismissible%2Freact-client)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
23
11
|
|
|
24
12
|
## Features
|
|
25
13
|
|
|
26
|
-
- **Easy to use** - Simple component API for dismissible content
|
|
27
|
-
- **Persistent state** - Dismissal state is saved and restored across sessions
|
|
28
|
-
- **Restore support** - Restore previously dismissed items programmatically
|
|
29
|
-
- **JWT Authentication** - Built-in support for secure JWT-based authentication
|
|
30
|
-
- **Customizable** - Custom loading, error, and dismiss button components
|
|
31
|
-
- **Accessible** - Built with accessibility best practices
|
|
32
|
-
- **Hook-based** - Includes `useDismissibleItem` hook for custom implementations
|
|
33
|
-
- **Lightweight** - Minimal bundle size with tree-shaking support
|
|
34
|
-
- **TypeScript** - Full TypeScript support with complete type definitions
|
|
14
|
+
- 🎯 **Easy to use** - Simple component API for dismissible content
|
|
15
|
+
- 💾 **Persistent state** - Dismissal state is saved and restored across sessions
|
|
16
|
+
- 🔄 **Restore support** - Restore previously dismissed items programmatically
|
|
17
|
+
- 🔐 **JWT Authentication** - Built-in support for secure JWT-based authentication
|
|
18
|
+
- 🎨 **Customizable** - Custom loading, error, and dismiss button components
|
|
19
|
+
- ♿ **Accessible** - Built with accessibility best practices
|
|
20
|
+
- 🪝 **Hook-based** - Includes `useDismissibleItem` hook for custom implementations
|
|
21
|
+
- 📦 **Lightweight** - Minimal bundle size with tree-shaking support
|
|
22
|
+
- 🔧 **TypeScript** - Full TypeScript support with complete type definitions
|
|
23
|
+
- 🐳 **Self-hosted** - Works with your own Dismissible API server
|
|
35
24
|
|
|
36
25
|
## Installation
|
|
37
26
|
|
|
@@ -51,7 +40,7 @@ npm install react react-dom
|
|
|
51
40
|
|
|
52
41
|
### 1. Set up the Dismissible API Server
|
|
53
42
|
|
|
54
|
-
First, you need a
|
|
43
|
+
First, you need a Dismissible API server running. The easiest way is with Docker:
|
|
55
44
|
|
|
56
45
|
```yaml
|
|
57
46
|
# docker-compose.yml
|
|
@@ -63,29 +52,38 @@ services:
|
|
|
63
52
|
- '3001:3001'
|
|
64
53
|
environment:
|
|
65
54
|
DISMISSIBLE_PORT: 3001
|
|
66
|
-
|
|
55
|
+
DISMISSIBLE_POSTGRES_STORAGE_CONNECTION_STRING: postgresql://postgres:postgres@db:5432/dismissible
|
|
56
|
+
depends_on:
|
|
57
|
+
- db
|
|
67
58
|
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
db:
|
|
60
|
+
image: postgres:15
|
|
61
|
+
environment:
|
|
62
|
+
POSTGRES_USER: postgres
|
|
63
|
+
POSTGRES_PASSWORD: postgres
|
|
64
|
+
POSTGRES_DB: dismissible
|
|
65
|
+
volumes:
|
|
66
|
+
- postgres_data:/var/lib/postgresql/data
|
|
67
|
+
|
|
68
|
+
volumes:
|
|
69
|
+
postgres_data:
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
OR
|
|
73
|
-
|
|
74
72
|
```bash
|
|
75
|
-
docker
|
|
73
|
+
docker-compose up -d
|
|
76
74
|
```
|
|
77
75
|
|
|
78
|
-
See the [API Server documentation](https://github.com/DismissibleIo/dismissible-api) for more deployment options including NestJS integration
|
|
76
|
+
See the [API Server documentation](https://github.com/DismissibleIo/dismissible-api) for more deployment options including NestJS integration.
|
|
79
77
|
|
|
80
78
|
### 2. Configure the Provider
|
|
81
79
|
|
|
82
|
-
Wrap your app with `DismissibleProvider`. The `userId` prop is **required** to track
|
|
80
|
+
Wrap your app with `DismissibleProvider`. The `userId` prop is **required** to track dismissals per user:
|
|
83
81
|
|
|
84
82
|
```tsx
|
|
85
83
|
import { DismissibleProvider } from '@dismissible/react-client';
|
|
86
84
|
|
|
87
85
|
function App() {
|
|
88
|
-
const userId = getCurrentUserId();
|
|
86
|
+
const userId = getCurrentUserId(); // Get from your auth system
|
|
89
87
|
|
|
90
88
|
return (
|
|
91
89
|
<DismissibleProvider userId={userId} baseUrl="http://localhost:3001">
|
|
@@ -96,7 +94,6 @@ function App() {
|
|
|
96
94
|
```
|
|
97
95
|
|
|
98
96
|
### 3. Use Dismissible Components
|
|
99
|
-
Now wrap any component you want to be dismissible with the `<Dismissible>` component, and the `itemId`, along with the `userId`, will become the unique key that is tracked across sessions and devices.
|
|
100
97
|
|
|
101
98
|
```tsx
|
|
102
99
|
import { Dismissible } from '@dismissible/react-client';
|
|
@@ -245,11 +242,11 @@ For custom implementations and advanced use cases.
|
|
|
245
242
|
|
|
246
243
|
| Property | Type | Description |
|
|
247
244
|
|----------|------|-------------|
|
|
248
|
-
| `
|
|
245
|
+
| `dismissedOn` | `string \| null` | ISO date string when item was dismissed, or null |
|
|
249
246
|
| `dismiss` | `() => Promise<void>` | Function to dismiss the item |
|
|
250
247
|
| `restore` | `() => Promise<void>` | Function to restore a dismissed item |
|
|
251
248
|
| `isLoading` | `boolean` | Loading state indicator |
|
|
252
|
-
| `error` | `Error \|
|
|
249
|
+
| `error` | `Error \| null` | Error state, if any |
|
|
253
250
|
| `item` | `IDismissibleItem \| undefined` | The full dismissible item object |
|
|
254
251
|
|
|
255
252
|
#### Example
|
|
@@ -258,7 +255,7 @@ For custom implementations and advanced use cases.
|
|
|
258
255
|
import { useDismissibleItem } from '@dismissible/react-client';
|
|
259
256
|
|
|
260
257
|
function CustomDismissible({ itemId, children }) {
|
|
261
|
-
const {
|
|
258
|
+
const { dismissedOn, dismiss, restore, isLoading, error } = useDismissibleItem(itemId);
|
|
262
259
|
|
|
263
260
|
if (isLoading) {
|
|
264
261
|
return <div>Loading...</div>;
|
|
@@ -268,7 +265,7 @@ function CustomDismissible({ itemId, children }) {
|
|
|
268
265
|
return <div>Error: {error.message}</div>;
|
|
269
266
|
}
|
|
270
267
|
|
|
271
|
-
if (
|
|
268
|
+
if (dismissedOn) {
|
|
272
269
|
return (
|
|
273
270
|
<div>
|
|
274
271
|
<p>This item was dismissed.</p>
|
|
@@ -341,6 +338,7 @@ function App() {
|
|
|
341
338
|
function Dashboard() {
|
|
342
339
|
return (
|
|
343
340
|
<div>
|
|
341
|
+
{/* Dismissible state is tracked per user */}
|
|
344
342
|
<Dismissible itemId="user-welcome-banner">
|
|
345
343
|
<div className="alert alert-info">
|
|
346
344
|
<h4>Welcome back!</h4>
|
|
@@ -429,19 +427,19 @@ function Dashboard() {
|
|
|
429
427
|
<div>
|
|
430
428
|
<Dismissible itemId="feature-announcement">
|
|
431
429
|
<div className="alert alert-success">
|
|
432
|
-
New feature: Dark mode is now available!
|
|
430
|
+
🎉 New feature: Dark mode is now available!
|
|
433
431
|
</div>
|
|
434
432
|
</Dismissible>
|
|
435
433
|
|
|
436
434
|
<Dismissible itemId="maintenance-notice">
|
|
437
435
|
<div className="alert alert-warning">
|
|
438
|
-
Scheduled maintenance: Sunday 2AM-4AM EST
|
|
436
|
+
⚠️ Scheduled maintenance: Sunday 2AM-4AM EST
|
|
439
437
|
</div>
|
|
440
438
|
</Dismissible>
|
|
441
439
|
|
|
442
440
|
<Dismissible itemId="survey-request">
|
|
443
441
|
<div className="alert alert-info">
|
|
444
|
-
Help us improve! Take our 2-minute survey.
|
|
442
|
+
📝 Help us improve! Take our 2-minute survey.
|
|
445
443
|
</div>
|
|
446
444
|
</Dismissible>
|
|
447
445
|
</div>
|
|
@@ -470,6 +468,74 @@ function RobustBanner() {
|
|
|
470
468
|
}
|
|
471
469
|
```
|
|
472
470
|
|
|
471
|
+
### Integration with Auth Providers
|
|
472
|
+
|
|
473
|
+
```tsx
|
|
474
|
+
import { DismissibleProvider } from '@dismissible/react-client';
|
|
475
|
+
|
|
476
|
+
// With Firebase Auth
|
|
477
|
+
function AppWithFirebase() {
|
|
478
|
+
const user = firebase.auth().currentUser;
|
|
479
|
+
|
|
480
|
+
return (
|
|
481
|
+
<DismissibleProvider
|
|
482
|
+
userId={user.uid}
|
|
483
|
+
jwt={async () => {
|
|
484
|
+
if (user) {
|
|
485
|
+
return await user.getIdToken();
|
|
486
|
+
}
|
|
487
|
+
throw new Error('User not authenticated');
|
|
488
|
+
}}
|
|
489
|
+
baseUrl="https://api.yourapp.com"
|
|
490
|
+
>
|
|
491
|
+
<YourApp />
|
|
492
|
+
</DismissibleProvider>
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// With Auth0
|
|
497
|
+
function AppWithAuth0() {
|
|
498
|
+
const { user, getAccessTokenSilently } = useAuth0();
|
|
499
|
+
|
|
500
|
+
return (
|
|
501
|
+
<DismissibleProvider
|
|
502
|
+
userId={user.sub}
|
|
503
|
+
jwt={async () => await getAccessTokenSilently()}
|
|
504
|
+
baseUrl="https://api.yourapp.com"
|
|
505
|
+
>
|
|
506
|
+
<YourApp />
|
|
507
|
+
</DismissibleProvider>
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// With token refresh logic
|
|
512
|
+
function AppWithTokenRefresh() {
|
|
513
|
+
const { user } = useAuth();
|
|
514
|
+
|
|
515
|
+
return (
|
|
516
|
+
<DismissibleProvider
|
|
517
|
+
userId={user.id}
|
|
518
|
+
jwt={async () => {
|
|
519
|
+
try {
|
|
520
|
+
const response = await fetch('/api/auth/refresh', {
|
|
521
|
+
method: 'POST',
|
|
522
|
+
credentials: 'include'
|
|
523
|
+
});
|
|
524
|
+
const { accessToken } = await response.json();
|
|
525
|
+
return accessToken;
|
|
526
|
+
} catch (error) {
|
|
527
|
+
console.error('Failed to refresh token:', error);
|
|
528
|
+
throw error;
|
|
529
|
+
}
|
|
530
|
+
}}
|
|
531
|
+
baseUrl="https://api.yourapp.com"
|
|
532
|
+
>
|
|
533
|
+
<YourApp />
|
|
534
|
+
</DismissibleProvider>
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
473
539
|
### Using the Hook for Complex Logic
|
|
474
540
|
|
|
475
541
|
```tsx
|
|
@@ -477,12 +543,12 @@ import { useDismissibleItem } from '@dismissible/react-client';
|
|
|
477
543
|
import { useState, useEffect } from 'react';
|
|
478
544
|
|
|
479
545
|
function SmartNotification({ itemId, message, type = 'info' }) {
|
|
480
|
-
const {
|
|
546
|
+
const { dismissedOn, dismiss, isLoading } = useDismissibleItem(itemId);
|
|
481
547
|
const [autoHide, setAutoHide] = useState(false);
|
|
482
548
|
|
|
483
549
|
// Auto-hide after 10 seconds for info messages
|
|
484
550
|
useEffect(() => {
|
|
485
|
-
if (type === 'info' && !
|
|
551
|
+
if (type === 'info' && !dismissedOn) {
|
|
486
552
|
const timer = setTimeout(() => {
|
|
487
553
|
setAutoHide(true);
|
|
488
554
|
dismiss();
|
|
@@ -490,9 +556,9 @@ function SmartNotification({ itemId, message, type = 'info' }) {
|
|
|
490
556
|
|
|
491
557
|
return () => clearTimeout(timer);
|
|
492
558
|
}
|
|
493
|
-
}, [type,
|
|
559
|
+
}, [type, dismissedOn, dismiss]);
|
|
494
560
|
|
|
495
|
-
if (
|
|
561
|
+
if (dismissedOn || autoHide) {
|
|
496
562
|
return null;
|
|
497
563
|
}
|
|
498
564
|
|
|
@@ -519,12 +585,12 @@ Use the `restore` function to bring back previously dismissed content:
|
|
|
519
585
|
import { useDismissibleItem } from '@dismissible/react-client';
|
|
520
586
|
|
|
521
587
|
function RestorableBanner({ itemId }) {
|
|
522
|
-
const {
|
|
588
|
+
const { dismissedOn, dismiss, restore, isLoading } = useDismissibleItem(itemId);
|
|
523
589
|
|
|
524
|
-
if (
|
|
590
|
+
if (dismissedOn) {
|
|
525
591
|
return (
|
|
526
592
|
<div className="dismissed-placeholder">
|
|
527
|
-
<p>Banner was dismissed on {new Date(
|
|
593
|
+
<p>Banner was dismissed on {new Date(dismissedOn).toLocaleDateString()}</p>
|
|
528
594
|
<button onClick={restore} disabled={isLoading}>
|
|
529
595
|
{isLoading ? 'Restoring...' : 'Show Banner Again'}
|
|
530
596
|
</button>
|
|
@@ -544,6 +610,51 @@ function RestorableBanner({ itemId }) {
|
|
|
544
610
|
}
|
|
545
611
|
```
|
|
546
612
|
|
|
613
|
+
### Admin Panel with Restore Capability
|
|
614
|
+
|
|
615
|
+
```tsx
|
|
616
|
+
import { useDismissibleItem } from '@dismissible/react-client';
|
|
617
|
+
|
|
618
|
+
function AdminNotificationManager({ notificationId }) {
|
|
619
|
+
const { dismissedOn, dismiss, restore, item, isLoading, error } =
|
|
620
|
+
useDismissibleItem(notificationId);
|
|
621
|
+
|
|
622
|
+
if (error) {
|
|
623
|
+
return <div className="error">Error: {error.message}</div>;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return (
|
|
627
|
+
<div className="admin-panel">
|
|
628
|
+
<h4>Notification: {notificationId}</h4>
|
|
629
|
+
<p>Status: {dismissedOn ? 'Dismissed' : 'Active'}</p>
|
|
630
|
+
{dismissedOn && (
|
|
631
|
+
<p>Dismissed at: {new Date(dismissedOn).toLocaleString()}</p>
|
|
632
|
+
)}
|
|
633
|
+
|
|
634
|
+
<div className="actions">
|
|
635
|
+
{dismissedOn ? (
|
|
636
|
+
<button
|
|
637
|
+
onClick={restore}
|
|
638
|
+
disabled={isLoading}
|
|
639
|
+
className="btn-restore"
|
|
640
|
+
>
|
|
641
|
+
Restore
|
|
642
|
+
</button>
|
|
643
|
+
) : (
|
|
644
|
+
<button
|
|
645
|
+
onClick={dismiss}
|
|
646
|
+
disabled={isLoading}
|
|
647
|
+
className="btn-dismiss"
|
|
648
|
+
>
|
|
649
|
+
Dismiss
|
|
650
|
+
</button>
|
|
651
|
+
)}
|
|
652
|
+
</div>
|
|
653
|
+
</div>
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
```
|
|
657
|
+
|
|
547
658
|
## Styling
|
|
548
659
|
|
|
549
660
|
The library includes minimal default styles. You can override them or provide your own:
|
|
@@ -567,6 +678,35 @@ The library includes minimal default styles. You can override them or provide yo
|
|
|
567
678
|
}
|
|
568
679
|
```
|
|
569
680
|
|
|
681
|
+
## TypeScript Support
|
|
682
|
+
|
|
683
|
+
The library is written in TypeScript and exports all type definitions:
|
|
684
|
+
|
|
685
|
+
```tsx
|
|
686
|
+
import type {
|
|
687
|
+
DismissibleProps,
|
|
688
|
+
DismissibleProviderProps,
|
|
689
|
+
JwtToken,
|
|
690
|
+
} from '@dismissible/react-client';
|
|
691
|
+
|
|
692
|
+
// Custom provider wrapper
|
|
693
|
+
const AuthenticatedDismissibleProvider: React.FC<{
|
|
694
|
+
children: React.ReactNode;
|
|
695
|
+
}> = ({ children }) => {
|
|
696
|
+
const { user, getToken } = useAuth();
|
|
697
|
+
|
|
698
|
+
return (
|
|
699
|
+
<DismissibleProvider
|
|
700
|
+
userId={user.id}
|
|
701
|
+
jwt={getToken}
|
|
702
|
+
baseUrl={process.env.DISMISSIBLE_API_URL}
|
|
703
|
+
>
|
|
704
|
+
{children}
|
|
705
|
+
</DismissibleProvider>
|
|
706
|
+
);
|
|
707
|
+
};
|
|
708
|
+
```
|
|
709
|
+
|
|
570
710
|
## Self-Hosting
|
|
571
711
|
|
|
572
712
|
Dismissible is designed to be self-hosted. You have full control over your data.
|
|
@@ -593,9 +733,9 @@ See the [NestJS documentation](https://dismissible.io/docs/nestjs) for setup ins
|
|
|
593
733
|
|
|
594
734
|
## Support
|
|
595
735
|
|
|
596
|
-
- [Documentation](https://dismissible.io/docs)
|
|
597
|
-
- [GitHub - React Client](https://github.com/DismissibleIo/dismissible-react-client)
|
|
598
|
-
- [GitHub - API Server](https://github.com/DismissibleIo/dismissible-api)
|
|
736
|
+
- 📖 [Documentation](https://dismissible.io/docs)
|
|
737
|
+
- 🐙 [GitHub - React Client](https://github.com/DismissibleIo/dismissible-react-client)
|
|
738
|
+
- 🐙 [GitHub - API Server](https://github.com/DismissibleIo/dismissible-api)
|
|
599
739
|
|
|
600
740
|
## License
|
|
601
741
|
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API configuration settings
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Configuration interface for API settings
|
|
6
|
+
*/
|
|
1
7
|
export interface ApiConfig {
|
|
2
8
|
/** Base URL for API requests */
|
|
3
9
|
baseUrl: string;
|
|
4
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Returns the API configuration based on the current environment
|
|
13
|
+
* @returns The API configuration object
|
|
14
|
+
*/
|
|
5
15
|
export declare const getConfig: () => ApiConfig;
|
|
16
|
+
/**
|
|
17
|
+
* Gets the API base URL
|
|
18
|
+
* @returns The base URL for API requests
|
|
19
|
+
*/
|
|
6
20
|
export declare const getBaseUrl: () => string;
|
|
7
21
|
export default getConfig;
|