@novu/react 2.0.0-alpha.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 +145 -0
- package/dist/client/index.d.mts +50 -0
- package/dist/client/index.d.ts +50 -0
- package/dist/client/index.js +244 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +244 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/server/server.d.mts +38 -0
- package/dist/server/server.d.ts +38 -0
- package/dist/server/server.js +44 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/server.mjs +16 -0
- package/dist/server/server.mjs.map +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Novu's React SDK for building custom inbox notification experiences.
|
|
2
|
+
|
|
3
|
+
Novu provides the `@novu/react` a React library that helps to add a fully functioning Inbox to your web application in minutes. Let's do a quick recap on how you can easily use it in your application.
|
|
4
|
+
See full documentation [here](https://docs.novu.co/inbox/react/get-started).
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
- Install `@novu/react` npm package in your react app
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @novu/react
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Getting Started
|
|
15
|
+
|
|
16
|
+
- Add the below code in the app.tsx file
|
|
17
|
+
|
|
18
|
+
```jsx
|
|
19
|
+
import { Inbox } from '@novu/react';
|
|
20
|
+
|
|
21
|
+
function Novu() {
|
|
22
|
+
return (
|
|
23
|
+
<Inbox
|
|
24
|
+
options={{
|
|
25
|
+
subscriberId: 'SUBSCRIBER_ID',
|
|
26
|
+
applicationIdentifier: 'APPLICATION_IDENTIFIER',
|
|
27
|
+
}}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Use your own backend and socket URL
|
|
34
|
+
|
|
35
|
+
By default, Novu's hosted services for API and socket are used. If you want, you can override them and configure your own.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { Inbox } from '@novu/react';
|
|
39
|
+
|
|
40
|
+
function Novu() {
|
|
41
|
+
return (
|
|
42
|
+
<Inbox
|
|
43
|
+
options={{
|
|
44
|
+
backendUrl: 'YOUR_BACKEND_URL',
|
|
45
|
+
socketUrl: 'YOUR_SOCKET_URL',
|
|
46
|
+
subscriberId: 'SUBSCRIBER_ID',
|
|
47
|
+
applicationIdentifier: 'APPLICATION_IDENTIFIER',
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Controlled Inbox
|
|
55
|
+
|
|
56
|
+
You can use the `open` prop to manage the Inbox popover open state.
|
|
57
|
+
|
|
58
|
+
```jsx
|
|
59
|
+
import { Inbox } from '@novu/react';
|
|
60
|
+
|
|
61
|
+
function Novu() {
|
|
62
|
+
const [open, setOpen] = useState(false);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div>
|
|
66
|
+
<Inbox
|
|
67
|
+
options={{
|
|
68
|
+
subscriberId: 'SUBSCRIBER_ID',
|
|
69
|
+
applicationIdentifier: 'APPLICATION_IDENTIFIER',
|
|
70
|
+
}}
|
|
71
|
+
open={isOpen}
|
|
72
|
+
/>
|
|
73
|
+
<button onClick={() => setOpen(true)}>Open Inbox</button>
|
|
74
|
+
<button onClick={() => setOpen(false)}>Close Inbox</button>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Localization
|
|
81
|
+
|
|
82
|
+
You can pass the `localization` prop to the Inbox component to change the language of the Inbox.
|
|
83
|
+
|
|
84
|
+
```jsx
|
|
85
|
+
import { Inbox } from '@novu/react';
|
|
86
|
+
|
|
87
|
+
function Novu() {
|
|
88
|
+
return (
|
|
89
|
+
<Inbox
|
|
90
|
+
options={{
|
|
91
|
+
subscriberId: 'SUBSCRIBER_ID',
|
|
92
|
+
applicationIdentifier: 'APPLICATION_IDENTIFIER',
|
|
93
|
+
}}
|
|
94
|
+
localization={{
|
|
95
|
+
'inbox.status.archived': 'Archived',
|
|
96
|
+
'inbox.status.unread': 'Unread',
|
|
97
|
+
'inbox.status.options.archived': 'Archived',
|
|
98
|
+
'inbox.status.options.unread': 'Unread',
|
|
99
|
+
'inbox.status.options.unreadRead': 'Unread/Read',
|
|
100
|
+
'inbox.status.unreadRead': 'Unread/Read',
|
|
101
|
+
'inbox.title': 'Inbox',
|
|
102
|
+
'notifications.emptyNotice': 'No notifications',
|
|
103
|
+
locale: 'en-US',
|
|
104
|
+
}}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## HMAC Encryption
|
|
111
|
+
|
|
112
|
+
When Novu's user adds the Inbox to their application they are required to pass a `subscriberId` which identifies the user's end-customer, and the application Identifier which is acted as a public key to communicate with the notification feed API.
|
|
113
|
+
|
|
114
|
+
A malicious actor can access the user feed by accessing the API and passing another `subscriberId` using the public application identifier.
|
|
115
|
+
|
|
116
|
+
HMAC encryption will make sure that a `subscriberId` is encrypted using the secret API key, and those will prevent malicious actors from impersonating users.
|
|
117
|
+
|
|
118
|
+
### Enabling HMAC Encryption
|
|
119
|
+
|
|
120
|
+
In order to enable Hash-Based Message Authentication Codes, you need to visit the admin panel In-App settings page and enable HMAC encryption for your environment.
|
|
121
|
+
|
|
122
|
+
<Frame caption="How to enable HMAC encryption for In-App Inbox">
|
|
123
|
+
<img src="/images/notification-center/client/react/get-started/hmac-encryption-enable.png" />
|
|
124
|
+
</Frame>
|
|
125
|
+
|
|
126
|
+
1. Next step would be to generate an HMAC encrypted subscriberId on your backend:
|
|
127
|
+
|
|
128
|
+
```jsx
|
|
129
|
+
import { createHmac } from 'crypto';
|
|
130
|
+
|
|
131
|
+
const hmacHash = createHmac('sha256', process.env.NOVU_API_KEY).update(subscriberId).digest('hex');
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
2. Then pass the created HMAC to your client side application forward it to the component:
|
|
135
|
+
|
|
136
|
+
```jsx
|
|
137
|
+
<Inbox
|
|
138
|
+
subscriberId={'SUBSCRIBER_ID_PLAIN_VALUE'}
|
|
139
|
+
subscriberHash={'SUBSCRIBER_ID_HASH_VALUE'}
|
|
140
|
+
applicationIdentifier={'APPLICATION_IDENTIFIER'}
|
|
141
|
+
/>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
> Note: If HMAC encryption is active in In-App provider settings and `subscriberHash`
|
|
145
|
+
> along with `subscriberId` is not provided, then Inbox will not load
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React$1 from 'react';
|
|
3
|
+
import { Notification, NotificationClickHandler, NotificationActionClickHandler, BaseNovuUIOptions } from '@novu/js/ui';
|
|
4
|
+
export { Notification } from '@novu/js/ui';
|
|
5
|
+
|
|
6
|
+
type BellRenderProps = ({ unreadCount }: {
|
|
7
|
+
unreadCount: number;
|
|
8
|
+
}) => React$1.ReactNode;
|
|
9
|
+
type BellProps = {
|
|
10
|
+
children?: never | BellRenderProps;
|
|
11
|
+
};
|
|
12
|
+
declare const Bell: React$1.MemoExoticComponent<(props: BellProps) => react_jsx_runtime.JSX.Element>;
|
|
13
|
+
|
|
14
|
+
type NotificationsRenderProps = (args: {
|
|
15
|
+
notification: Notification;
|
|
16
|
+
}) => React.ReactNode;
|
|
17
|
+
type DefaultInboxProps = {
|
|
18
|
+
open?: boolean;
|
|
19
|
+
renderNotification?: (args: {
|
|
20
|
+
notification: Notification;
|
|
21
|
+
}) => React.ReactNode;
|
|
22
|
+
renderBell?: ({ unreadCount }: {
|
|
23
|
+
unreadCount: number;
|
|
24
|
+
}) => React.ReactNode;
|
|
25
|
+
onNotificationClick?: NotificationClickHandler;
|
|
26
|
+
onPrimaryActionClick?: NotificationActionClickHandler;
|
|
27
|
+
onSecondaryActionClick?: NotificationActionClickHandler;
|
|
28
|
+
};
|
|
29
|
+
type BaseProps = BaseNovuUIOptions;
|
|
30
|
+
type DefaultProps = BaseProps & DefaultInboxProps & {
|
|
31
|
+
children?: never;
|
|
32
|
+
};
|
|
33
|
+
type WithChildrenProps = BaseProps & {
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type InboxProps = DefaultProps | WithChildrenProps;
|
|
38
|
+
declare const Inbox: React$1.MemoExoticComponent<(props: InboxProps) => react_jsx_runtime.JSX.Element>;
|
|
39
|
+
|
|
40
|
+
declare const Preferences: () => react_jsx_runtime.JSX.Element;
|
|
41
|
+
|
|
42
|
+
type NotificationProps = {
|
|
43
|
+
children?: never | NotificationsRenderProps;
|
|
44
|
+
onNotificationClick?: NotificationClickHandler;
|
|
45
|
+
onPrimaryActionClick?: NotificationActionClickHandler;
|
|
46
|
+
onSecondaryActionClick?: NotificationActionClickHandler;
|
|
47
|
+
};
|
|
48
|
+
declare const Notifications: React$1.MemoExoticComponent<({ children, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick }: NotificationProps) => react_jsx_runtime.JSX.Element>;
|
|
49
|
+
|
|
50
|
+
export { type BaseProps, Bell, type BellProps, type BellRenderProps, type DefaultInboxProps, type DefaultProps, Inbox, type InboxProps, type NotificationProps, Notifications, type NotificationsRenderProps, Preferences, type WithChildrenProps };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React$1 from 'react';
|
|
3
|
+
import { Notification, NotificationClickHandler, NotificationActionClickHandler, BaseNovuUIOptions } from '@novu/js/ui';
|
|
4
|
+
export { Notification } from '@novu/js/ui';
|
|
5
|
+
|
|
6
|
+
type BellRenderProps = ({ unreadCount }: {
|
|
7
|
+
unreadCount: number;
|
|
8
|
+
}) => React$1.ReactNode;
|
|
9
|
+
type BellProps = {
|
|
10
|
+
children?: never | BellRenderProps;
|
|
11
|
+
};
|
|
12
|
+
declare const Bell: React$1.MemoExoticComponent<(props: BellProps) => react_jsx_runtime.JSX.Element>;
|
|
13
|
+
|
|
14
|
+
type NotificationsRenderProps = (args: {
|
|
15
|
+
notification: Notification;
|
|
16
|
+
}) => React.ReactNode;
|
|
17
|
+
type DefaultInboxProps = {
|
|
18
|
+
open?: boolean;
|
|
19
|
+
renderNotification?: (args: {
|
|
20
|
+
notification: Notification;
|
|
21
|
+
}) => React.ReactNode;
|
|
22
|
+
renderBell?: ({ unreadCount }: {
|
|
23
|
+
unreadCount: number;
|
|
24
|
+
}) => React.ReactNode;
|
|
25
|
+
onNotificationClick?: NotificationClickHandler;
|
|
26
|
+
onPrimaryActionClick?: NotificationActionClickHandler;
|
|
27
|
+
onSecondaryActionClick?: NotificationActionClickHandler;
|
|
28
|
+
};
|
|
29
|
+
type BaseProps = BaseNovuUIOptions;
|
|
30
|
+
type DefaultProps = BaseProps & DefaultInboxProps & {
|
|
31
|
+
children?: never;
|
|
32
|
+
};
|
|
33
|
+
type WithChildrenProps = BaseProps & {
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type InboxProps = DefaultProps | WithChildrenProps;
|
|
38
|
+
declare const Inbox: React$1.MemoExoticComponent<(props: InboxProps) => react_jsx_runtime.JSX.Element>;
|
|
39
|
+
|
|
40
|
+
declare const Preferences: () => react_jsx_runtime.JSX.Element;
|
|
41
|
+
|
|
42
|
+
type NotificationProps = {
|
|
43
|
+
children?: never | NotificationsRenderProps;
|
|
44
|
+
onNotificationClick?: NotificationClickHandler;
|
|
45
|
+
onPrimaryActionClick?: NotificationActionClickHandler;
|
|
46
|
+
onSecondaryActionClick?: NotificationActionClickHandler;
|
|
47
|
+
};
|
|
48
|
+
declare const Notifications: React$1.MemoExoticComponent<({ children, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick }: NotificationProps) => react_jsx_runtime.JSX.Element>;
|
|
49
|
+
|
|
50
|
+
export { type BaseProps, Bell, type BellProps, type BellRenderProps, type DefaultInboxProps, type DefaultProps, Inbox, type InboxProps, type NotificationProps, Notifications, type NotificationsRenderProps, Preferences, type WithChildrenProps };
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/components/Bell.tsx
|
|
2
|
+
var _react = require('react'); var _react2 = _interopRequireDefault(_react);
|
|
3
|
+
|
|
4
|
+
// src/utils/createContextAndHook.ts
|
|
5
|
+
|
|
6
|
+
function assertContextExists(contextVal, msgOrCtx) {
|
|
7
|
+
if (!contextVal) {
|
|
8
|
+
throw typeof msgOrCtx === "string" ? new Error(msgOrCtx) : new Error(`${msgOrCtx.displayName} not found`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
var createContextAndHook = (displayName, options) => {
|
|
12
|
+
const { assertCtxFn = assertContextExists } = options || {};
|
|
13
|
+
const Ctx = _react2.default.createContext(void 0);
|
|
14
|
+
Ctx.displayName = displayName;
|
|
15
|
+
const useCtx = () => {
|
|
16
|
+
const ctx = _react2.default.useContext(Ctx);
|
|
17
|
+
assertCtxFn(ctx, `Component must be wrapped with the <Inbox /> Component`);
|
|
18
|
+
return ctx.value;
|
|
19
|
+
};
|
|
20
|
+
const useCtxWithoutGuarantee = () => {
|
|
21
|
+
const ctx = _react2.default.useContext(Ctx);
|
|
22
|
+
return ctx ? ctx.value : {};
|
|
23
|
+
};
|
|
24
|
+
return [Ctx, useCtx, useCtxWithoutGuarantee];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// src/context/RenderContext.tsx
|
|
28
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
29
|
+
var [RendererContext, useRendererContext] = createContextAndHook("RendererContext");
|
|
30
|
+
var RendererProvider = (props) => {
|
|
31
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, RendererContext.Provider, { value: { value: props.value }, children: props.children });
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/components/Mounter.tsx
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
function Mounter({ mount }) {
|
|
38
|
+
const ref = _react2.default.useRef(null);
|
|
39
|
+
_react2.default.useEffect(() => {
|
|
40
|
+
let unmount;
|
|
41
|
+
const element = ref.current;
|
|
42
|
+
if (element && mount) {
|
|
43
|
+
const possibleUnmount = mount(element);
|
|
44
|
+
if (possibleUnmount) {
|
|
45
|
+
unmount = possibleUnmount;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return () => {
|
|
49
|
+
if (element && unmount) {
|
|
50
|
+
unmount(element);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}, [ref, mount]);
|
|
54
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { ref });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/components/Bell.tsx
|
|
58
|
+
|
|
59
|
+
var Bell = _react2.default.memo((props) => {
|
|
60
|
+
const { novuUI, mountElement } = useRendererContext();
|
|
61
|
+
const mount = _react2.default.useCallback(
|
|
62
|
+
(element) => {
|
|
63
|
+
return novuUI.mountComponent({
|
|
64
|
+
name: "Bell",
|
|
65
|
+
element,
|
|
66
|
+
props: props.children ? { mountBell: (el, { unreadCount }) => mountElement(el, _optionalChain([props, 'access', _ => _.children, 'optionalCall', _2 => _2({ unreadCount })])) } : void 0
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
[props.children]
|
|
70
|
+
);
|
|
71
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Mounter, { mount });
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// src/components/Inbox.tsx
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
// src/components/Renderer.tsx
|
|
78
|
+
|
|
79
|
+
var _reactdom = require('react-dom'); var _reactdom2 = _interopRequireDefault(_reactdom);
|
|
80
|
+
var _ui = require('@novu/js/ui');
|
|
81
|
+
|
|
82
|
+
// src/hooks/useDataRef.ts
|
|
83
|
+
|
|
84
|
+
var useDataRef = (data) => {
|
|
85
|
+
const ref = _react.useRef.call(void 0, data);
|
|
86
|
+
ref.current = data;
|
|
87
|
+
return ref;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// src/components/Renderer.tsx
|
|
91
|
+
|
|
92
|
+
var Renderer = ({ options, children }) => {
|
|
93
|
+
const optionsRef = useDataRef(options);
|
|
94
|
+
const [novuUI, setNovuUI] = _react.useState.call(void 0, );
|
|
95
|
+
const [mountedElements, setMountedElements] = _react.useState.call(void 0, /* @__PURE__ */ new Map());
|
|
96
|
+
const mountElement = _react.useCallback.call(void 0,
|
|
97
|
+
(el, mountedElement) => {
|
|
98
|
+
setMountedElements((prev) => {
|
|
99
|
+
const newMountedElements = new Map(prev);
|
|
100
|
+
newMountedElements.set(el, mountedElement);
|
|
101
|
+
return newMountedElements;
|
|
102
|
+
});
|
|
103
|
+
return () => {
|
|
104
|
+
setMountedElements((prev) => {
|
|
105
|
+
const newMountedElements = new Map(prev);
|
|
106
|
+
newMountedElements.delete(el);
|
|
107
|
+
return newMountedElements;
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
[setMountedElements]
|
|
112
|
+
);
|
|
113
|
+
_react.useEffect.call(void 0, () => {
|
|
114
|
+
const novu = new (0, _ui.NovuUI)(optionsRef.current);
|
|
115
|
+
setNovuUI(novu);
|
|
116
|
+
return () => {
|
|
117
|
+
novu.unmount();
|
|
118
|
+
};
|
|
119
|
+
}, []);
|
|
120
|
+
_react.useEffect.call(void 0, () => {
|
|
121
|
+
if (!novuUI) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
novuUI.updateAppearance(options.appearance);
|
|
125
|
+
novuUI.updateLocalization(options.localization);
|
|
126
|
+
novuUI.updateTabs(options.tabs);
|
|
127
|
+
novuUI.updateOptions(options.options);
|
|
128
|
+
}, [options]);
|
|
129
|
+
if (!novuUI) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, RendererProvider, { value: { mountElement, novuUI }, children: [
|
|
133
|
+
[...mountedElements].map(([element, mountedElement]) => {
|
|
134
|
+
return _reactdom2.default.createPortal(mountedElement, element);
|
|
135
|
+
}),
|
|
136
|
+
children
|
|
137
|
+
] });
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// src/components/Inbox.tsx
|
|
141
|
+
|
|
142
|
+
var DefaultInbox = ({
|
|
143
|
+
open,
|
|
144
|
+
renderNotification,
|
|
145
|
+
renderBell,
|
|
146
|
+
onNotificationClick,
|
|
147
|
+
onPrimaryActionClick,
|
|
148
|
+
onSecondaryActionClick
|
|
149
|
+
}) => {
|
|
150
|
+
const { novuUI, mountElement } = useRendererContext();
|
|
151
|
+
const mount = _react2.default.useCallback(
|
|
152
|
+
(element) => {
|
|
153
|
+
return novuUI.mountComponent({
|
|
154
|
+
name: "Inbox",
|
|
155
|
+
props: {
|
|
156
|
+
open,
|
|
157
|
+
mountNotification: renderNotification ? (el, { notification }) => mountElement(el, renderNotification({ notification })) : void 0,
|
|
158
|
+
mountBell: renderBell ? (el, { unreadCount }) => mountElement(el, renderBell({ unreadCount })) : void 0,
|
|
159
|
+
onNotificationClick,
|
|
160
|
+
onPrimaryActionClick,
|
|
161
|
+
onSecondaryActionClick
|
|
162
|
+
},
|
|
163
|
+
element
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
[open, renderNotification, renderBell, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick]
|
|
167
|
+
);
|
|
168
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Mounter, { mount });
|
|
169
|
+
};
|
|
170
|
+
var Inbox = _react2.default.memo((props) => {
|
|
171
|
+
if (isWithChildrenProps(props)) {
|
|
172
|
+
const { children, ...options2 } = props;
|
|
173
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Renderer, { options: options2, children });
|
|
174
|
+
}
|
|
175
|
+
const {
|
|
176
|
+
open,
|
|
177
|
+
renderNotification,
|
|
178
|
+
renderBell,
|
|
179
|
+
onNotificationClick,
|
|
180
|
+
onPrimaryActionClick,
|
|
181
|
+
onSecondaryActionClick,
|
|
182
|
+
...options
|
|
183
|
+
} = props;
|
|
184
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Renderer, { options, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
185
|
+
DefaultInbox,
|
|
186
|
+
{
|
|
187
|
+
open,
|
|
188
|
+
renderNotification,
|
|
189
|
+
renderBell,
|
|
190
|
+
onNotificationClick,
|
|
191
|
+
onPrimaryActionClick,
|
|
192
|
+
onSecondaryActionClick
|
|
193
|
+
}
|
|
194
|
+
) });
|
|
195
|
+
});
|
|
196
|
+
function isWithChildrenProps(props) {
|
|
197
|
+
return "children" in props;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/components/Preferences.tsx
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
var Preferences = () => {
|
|
204
|
+
const { novuUI } = useRendererContext();
|
|
205
|
+
const mount = _react2.default.useCallback((element) => {
|
|
206
|
+
return novuUI.mountComponent({
|
|
207
|
+
name: "Preferences",
|
|
208
|
+
element
|
|
209
|
+
});
|
|
210
|
+
}, []);
|
|
211
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Mounter, { mount });
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// src/components/Notifications.tsx
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
var Notifications = _react2.default.memo(
|
|
218
|
+
({ children, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick }) => {
|
|
219
|
+
const { novuUI, mountElement } = useRendererContext();
|
|
220
|
+
const mount = _react2.default.useCallback(
|
|
221
|
+
(element) => {
|
|
222
|
+
return novuUI.mountComponent({
|
|
223
|
+
name: "Notifications",
|
|
224
|
+
element,
|
|
225
|
+
props: children ? {
|
|
226
|
+
mountNotification: (el, { notification }) => mountElement(el, _optionalChain([children, 'optionalCall', _3 => _3({ notification })])),
|
|
227
|
+
onNotificationClick,
|
|
228
|
+
onPrimaryActionClick,
|
|
229
|
+
onSecondaryActionClick
|
|
230
|
+
} : { onNotificationClick, onPrimaryActionClick, onSecondaryActionClick }
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
[children, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick]
|
|
234
|
+
);
|
|
235
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Mounter, { mount });
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
exports.Bell = Bell; exports.Inbox = Inbox; exports.Notifications = Notifications; exports.Preferences = Preferences;
|
|
244
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/pavlotymchuk/projects/js/novu-2/packages/react/dist/client/index.js","../../src/components/Bell.tsx","../../src/utils/createContextAndHook.ts","../../src/context/RenderContext.tsx","../../src/components/Mounter.tsx","../../src/components/Inbox.tsx","../../src/components/Renderer.tsx","../../src/hooks/useDataRef.ts","../../src/components/Preferences.tsx","../../src/components/Notifications.tsx"],"names":[],"mappings":"AAAA;ACAA,4EAAkB;ADElB;AACA;AEHA;AAEO,SAAS,mBAAA,CAAoB,UAAA,EAAqB,QAAA,EAA2D;AAClH,EAAA,GAAA,CAAI,CAAC,UAAA,EAAY;AACf,IAAA,MAAM,OAAO,SAAA,IAAa,SAAA,EAAW,IAAI,KAAA,CAAM,QAAQ,EAAA,EAAI,IAAI,KAAA,CAAM,CAAA,EAAA;AACvE,EAAA;AACF;AAegF;AACpB,EAAA;AACc,EAAA;AACtD,EAAA;AAEG,EAAA;AACa,IAAA;AACf,IAAA;AAEG,IAAA;AACtB,EAAA;AAEqC,EAAA;AACH,IAAA;AAEN,IAAA;AAC5B,EAAA;AAE2C,EAAA;AAC7C;AFf0E;AACA;AGXjE;AAHgF;AAEK;AAC3C,EAAA;AACnD;AHgB0E;AACA;AIjCxD;AA6BT;AApBwC;AACF,EAAA;AAEvB,EAAA;AAChB,IAAA;AACgB,IAAA;AACE,IAAA;AACiB,MAAA;AAChB,MAAA;AACT,QAAA;AACZ,MAAA;AACF,IAAA;AAEa,IAAA;AACa,MAAA;AACP,QAAA;AACjB,MAAA;AACF,IAAA;AACa,EAAA;AAEO,EAAA;AACxB;AJyB0E;AACA;AC9BjE;AAhB4C;AACN,EAAA;AAEzB,EAAA;AACQ,IAAA;AACK,MAAA;AACrB,QAAA;AACN,QAAA;AAE0C,QAAA;AAE3C,MAAA;AACH,IAAA;AACe,IAAA;AACjB,EAAA;AAE8B,EAAA;AAC/B;AD6CyE;AACA;AKzExD;AL2EwD;AACA;AM5ElB;AACnC;AACE;AN8EmD;AACA;AOjFnD;AAEmB;AACjB,EAAA;AACT,EAAA;AAEP,EAAA;AACT;APiF0E;AACA;AMvBtE;AAnD8D;AAC3B,EAAA;AACoB,EAAA;AACF,EAAA;AAElC,EAAA;AACkC,IAAA;AACtB,MAAA;AACY,QAAA;AACE,QAAA;AAElC,QAAA;AACR,MAAA;AAEY,MAAA;AACkB,QAAA;AACY,UAAA;AACX,UAAA;AAErB,UAAA;AACR,QAAA;AACH,MAAA;AACF,IAAA;AACmB,IAAA;AACrB,EAAA;AAEgB,EAAA;AAC4B,IAAA;AAC5B,IAAA;AAED,IAAA;AACE,MAAA;AACf,IAAA;AACG,EAAA;AAEW,EAAA;AACD,IAAA;AACX,MAAA;AACF,IAAA;AAE0C,IAAA;AACI,IAAA;AAChB,IAAA;AACM,IAAA;AAC1B,EAAA;AAEC,EAAA;AACJ,IAAA;AACT,EAAA;AAG2C,EAAA;AACkB,IAAA;AACH,MAAA;AACrD,IAAA;AAEA,IAAA;AACH,EAAA;AAEJ;ANgE0E;AACA;AKrGjE;AA9Ba;AACpB,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACuB;AACsB,EAAA;AAEzB,EAAA;AACQ,IAAA;AACK,MAAA;AACrB,QAAA;AACC,QAAA;AACL,UAAA;AAEwB,UAAA;AAEsC,UAAA;AAC9D,UAAA;AACA,UAAA;AACA,UAAA;AACF,QAAA;AACA,QAAA;AACD,MAAA;AACH,IAAA;AAC4D,IAAA;AAC9D,EAAA;AAE8B,EAAA;AAChC;AAEuD;AACrB,EAAA;AACG,IAAA;AAEY,IAAA;AAC/C,EAAA;AAEM,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACG,IAAA;AACD,EAAA;AAIA,EAAA;AAAC,IAAA;AAAA,IAAA;AACC,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AAAA,IAAA;AAEJ,EAAA;AAEH;AAE2E;AACrD,EAAA;AACvB;AL4H0E;AACA;AQvMxD;AAcT;AAVwB;AACA,EAAA;AAE2B,EAAA;AAC3B,IAAA;AACrB,MAAA;AACN,MAAA;AACD,IAAA;AACE,EAAA;AAEyB,EAAA;AAChC;ARqM0E;AACA;ASrNxD;AAmCP;AAtBwB;AACuB,EAAA;AACT,IAAA;AAEzB,IAAA;AACQ,MAAA;AACK,QAAA;AACrB,UAAA;AACN,UAAA;AAEI,UAAA;AACgE,YAAA;AAC9D,YAAA;AACA,YAAA;AACA,YAAA;AAE2C,UAAA;AAClD,QAAA;AACH,MAAA;AACsD,MAAA;AACxD,IAAA;AAE8B,IAAA;AAChC,EAAA;AACF;ATwM0E;AACA;AACA;AACA;AACA;AACA","file":"/Users/pavlotymchuk/projects/js/novu-2/packages/react/dist/client/index.js","sourcesContent":[null,"import React from 'react';\nimport { useRenderer } from '../context/RenderContext';\nimport { Mounter } from './Mounter';\n\nexport type BellRenderProps = ({ unreadCount }: { unreadCount: number }) => React.ReactNode;\n\nexport type BellProps = {\n children?: never | BellRenderProps;\n};\n\nexport const Bell = React.memo((props: BellProps) => {\n const { novuUI, mountElement } = useRenderer();\n\n const mount = React.useCallback(\n (element: HTMLElement) => {\n return novuUI.mountComponent({\n name: 'Bell',\n element,\n props: props.children\n ? { mountBell: (el, { unreadCount }) => mountElement(el, props.children?.({ unreadCount })) }\n : undefined,\n });\n },\n [props.children]\n );\n\n return <Mounter mount={mount} />;\n});\n","import React from 'react';\n\nexport function assertContextExists(contextVal: unknown, msgOrCtx: string | React.Context<any>): asserts contextVal {\n if (!contextVal) {\n throw typeof msgOrCtx === 'string' ? new Error(msgOrCtx) : new Error(`${msgOrCtx.displayName} not found`);\n }\n}\n\ntype Options = { assertCtxFn?: (v: unknown, msg: string) => void };\ntype ContextOf<T> = React.Context<{ value: T } | undefined>;\ntype UseCtxFn<T> = () => T;\n\n/**\n * Creates and returns a Context and two hooks that return the context value.\n * The Context type is derived from the type passed in by the user.\n * The first hook returned guarantees that the context exists so the returned value is always CtxValue\n * The second hook makes no guarantees, so the returned value can be CtxValue | undefined\n */\nexport const createContextAndHook = <CtxVal>(\n displayName: string,\n options?: Options\n): [ContextOf<CtxVal>, UseCtxFn<CtxVal>, UseCtxFn<CtxVal | Partial<CtxVal>>] => {\n const { assertCtxFn = assertContextExists } = options || {};\n const Ctx = React.createContext<{ value: CtxVal } | undefined>(undefined);\n Ctx.displayName = displayName;\n\n const useCtx = () => {\n const ctx = React.useContext(Ctx);\n assertCtxFn(ctx, `Component must be wrapped with the <Inbox /> Component`);\n\n return (ctx as any).value as CtxVal;\n };\n\n const useCtxWithoutGuarantee = () => {\n const ctx = React.useContext(Ctx);\n\n return ctx ? ctx.value : {};\n };\n\n return [Ctx, useCtx, useCtxWithoutGuarantee];\n};\n","import React from 'react';\nimport type { NovuUI } from '@novu/js/ui';\nimport { createContextAndHook } from '../utils/createContextAndHook';\n\nexport type MountedElement = React.ReactNode;\nexport type MountedElements = Map<HTMLElement, MountedElement>;\n\ntype RendererContextValue = {\n mountElement: (el: HTMLElement, mountedElement: MountedElement) => () => void;\n novuUI: NovuUI;\n};\n\nconst [RendererContext, useRendererContext] = createContextAndHook<RendererContextValue>('RendererContext');\n\nconst RendererProvider = (props: React.PropsWithChildren<{ value: RendererContextValue }>) => {\n return <RendererContext.Provider value={{ value: props.value }}>{props.children}</RendererContext.Provider>;\n};\n\nexport { useRendererContext as useRenderer, RendererProvider };\n","import React from 'react';\n\ntype MounterProps = {\n mount: (node: HTMLElement) => ((node: HTMLElement) => void) | void;\n};\n\n/**\n * Mounter allows you to mount a component to a DOM node.\n */\nexport function Mounter({ mount }: MounterProps) {\n const ref = React.useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n let unmount: (node: HTMLDivElement) => void | undefined;\n const element = ref.current;\n if (element && mount) {\n const possibleUnmount = mount(element);\n if (possibleUnmount) {\n unmount = possibleUnmount;\n }\n }\n\n return () => {\n if (element && unmount) {\n unmount(element);\n }\n };\n }, [ref, mount]);\n\n return <div ref={ref} />;\n}\n","import React from 'react';\nimport { useRenderer } from '../context/RenderContext';\nimport { DefaultProps, DefaultInboxProps, WithChildrenProps } from '../utils/types';\nimport { Mounter } from './Mounter';\nimport { Renderer } from './Renderer';\n\nexport type InboxProps = DefaultProps | WithChildrenProps;\n\nconst DefaultInbox = ({\n open,\n renderNotification,\n renderBell,\n onNotificationClick,\n onPrimaryActionClick,\n onSecondaryActionClick,\n}: DefaultInboxProps) => {\n const { novuUI, mountElement } = useRenderer();\n\n const mount = React.useCallback(\n (element: HTMLElement) => {\n return novuUI.mountComponent({\n name: 'Inbox',\n props: {\n open,\n mountNotification: renderNotification\n ? (el, { notification }) => mountElement(el, renderNotification({ notification }))\n : undefined,\n mountBell: renderBell ? (el, { unreadCount }) => mountElement(el, renderBell({ unreadCount })) : undefined,\n onNotificationClick,\n onPrimaryActionClick,\n onSecondaryActionClick,\n },\n element,\n });\n },\n [open, renderNotification, renderBell, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick]\n );\n\n return <Mounter mount={mount} />;\n};\n\nexport const Inbox = React.memo((props: InboxProps) => {\n if (isWithChildrenProps(props)) {\n const { children, ...options } = props;\n\n return <Renderer options={options}>{children}</Renderer>;\n }\n\n const {\n open,\n renderNotification,\n renderBell,\n onNotificationClick,\n onPrimaryActionClick,\n onSecondaryActionClick,\n ...options\n } = props;\n\n return (\n <Renderer options={options}>\n <DefaultInbox\n open={open}\n renderNotification={renderNotification}\n renderBell={renderBell}\n onNotificationClick={onNotificationClick}\n onPrimaryActionClick={onPrimaryActionClick}\n onSecondaryActionClick={onSecondaryActionClick}\n />\n </Renderer>\n );\n});\n\nfunction isWithChildrenProps(props: InboxProps): props is WithChildrenProps {\n return 'children' in props;\n}\n","import React, { useCallback, useEffect, useState } from 'react';\nimport ReactDOM from 'react-dom';\nimport { NovuUI } from '@novu/js/ui';\nimport type { NovuUIOptions } from '@novu/js/ui';\nimport { MountedElement, RendererProvider } from '../context/RenderContext';\nimport { useDataRef } from '../hooks/useDataRef';\n\ntype RendererProps = React.PropsWithChildren<{\n options: NovuUIOptions;\n}>;\n\n/**\n *\n * Renderer component that provides the NovuUI instance and mounts the elements on DOM in a portal\n */\nexport const Renderer = ({ options, children }: RendererProps) => {\n const optionsRef = useDataRef(options);\n const [novuUI, setNovuUI] = useState<NovuUI | undefined>();\n const [mountedElements, setMountedElements] = useState(new Map<HTMLElement, MountedElement>());\n\n const mountElement = useCallback(\n (el: HTMLElement, mountedElement: MountedElement) => {\n setMountedElements((prev) => {\n const newMountedElements = new Map(prev);\n newMountedElements.set(el, mountedElement);\n\n return newMountedElements;\n });\n\n return () => {\n setMountedElements((prev) => {\n const newMountedElements = new Map(prev);\n newMountedElements.delete(el);\n\n return newMountedElements;\n });\n };\n },\n [setMountedElements]\n );\n\n useEffect(() => {\n const novu = new NovuUI(optionsRef.current);\n setNovuUI(novu);\n\n return () => {\n novu.unmount();\n };\n }, []);\n\n useEffect(() => {\n if (!novuUI) {\n return;\n }\n\n novuUI.updateAppearance(options.appearance);\n novuUI.updateLocalization(options.localization);\n novuUI.updateTabs(options.tabs);\n novuUI.updateOptions(options.options);\n }, [options]);\n\n if (!novuUI) {\n return null;\n }\n\n return (\n <RendererProvider value={{ mountElement, novuUI }}>\n {[...mountedElements].map(([element, mountedElement]) => {\n return ReactDOM.createPortal(mountedElement, element);\n })}\n\n {children}\n </RendererProvider>\n );\n};\n","import { useRef } from 'react';\n\nexport const useDataRef = <T>(data: T) => {\n const ref = useRef(data);\n ref.current = data;\n\n return ref;\n};\n","import React from 'react';\nimport { useRenderer } from '../context/RenderContext';\nimport { Mounter } from './Mounter';\n\nexport const Preferences = () => {\n const { novuUI } = useRenderer();\n\n const mount = React.useCallback((element: HTMLElement) => {\n return novuUI.mountComponent({\n name: 'Preferences',\n element,\n });\n }, []);\n\n return <Mounter mount={mount} />;\n};\n","import React from 'react';\nimport { useRenderer } from '../context/RenderContext';\nimport { NotificationsRenderProps } from '../utils/types';\nimport { Mounter } from './Mounter';\nimport type { NotificationClickHandler, NotificationActionClickHandler } from '@novu/js/ui';\n\nexport type NotificationProps = {\n children?: never | NotificationsRenderProps;\n onNotificationClick?: NotificationClickHandler;\n onPrimaryActionClick?: NotificationActionClickHandler;\n onSecondaryActionClick?: NotificationActionClickHandler;\n};\n\nexport const Notifications = React.memo(\n ({ children, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick }: NotificationProps) => {\n const { novuUI, mountElement } = useRenderer();\n\n const mount = React.useCallback(\n (element: HTMLElement) => {\n return novuUI.mountComponent({\n name: 'Notifications',\n element,\n props: children\n ? {\n mountNotification: (el, { notification }) => mountElement(el, children?.({ notification })),\n onNotificationClick,\n onPrimaryActionClick,\n onSecondaryActionClick,\n }\n : { onNotificationClick, onPrimaryActionClick, onSecondaryActionClick },\n });\n },\n [children, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick]\n );\n\n return <Mounter mount={mount} />;\n }\n);\n"]}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// src/components/Bell.tsx
|
|
2
|
+
import React3 from "react";
|
|
3
|
+
|
|
4
|
+
// src/utils/createContextAndHook.ts
|
|
5
|
+
import React from "react";
|
|
6
|
+
function assertContextExists(contextVal, msgOrCtx) {
|
|
7
|
+
if (!contextVal) {
|
|
8
|
+
throw typeof msgOrCtx === "string" ? new Error(msgOrCtx) : new Error(`${msgOrCtx.displayName} not found`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
var createContextAndHook = (displayName, options) => {
|
|
12
|
+
const { assertCtxFn = assertContextExists } = options || {};
|
|
13
|
+
const Ctx = React.createContext(void 0);
|
|
14
|
+
Ctx.displayName = displayName;
|
|
15
|
+
const useCtx = () => {
|
|
16
|
+
const ctx = React.useContext(Ctx);
|
|
17
|
+
assertCtxFn(ctx, `Component must be wrapped with the <Inbox /> Component`);
|
|
18
|
+
return ctx.value;
|
|
19
|
+
};
|
|
20
|
+
const useCtxWithoutGuarantee = () => {
|
|
21
|
+
const ctx = React.useContext(Ctx);
|
|
22
|
+
return ctx ? ctx.value : {};
|
|
23
|
+
};
|
|
24
|
+
return [Ctx, useCtx, useCtxWithoutGuarantee];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// src/context/RenderContext.tsx
|
|
28
|
+
import { jsx } from "react/jsx-runtime";
|
|
29
|
+
var [RendererContext, useRendererContext] = createContextAndHook("RendererContext");
|
|
30
|
+
var RendererProvider = (props) => {
|
|
31
|
+
return /* @__PURE__ */ jsx(RendererContext.Provider, { value: { value: props.value }, children: props.children });
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/components/Mounter.tsx
|
|
35
|
+
import React2 from "react";
|
|
36
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
37
|
+
function Mounter({ mount }) {
|
|
38
|
+
const ref = React2.useRef(null);
|
|
39
|
+
React2.useEffect(() => {
|
|
40
|
+
let unmount;
|
|
41
|
+
const element = ref.current;
|
|
42
|
+
if (element && mount) {
|
|
43
|
+
const possibleUnmount = mount(element);
|
|
44
|
+
if (possibleUnmount) {
|
|
45
|
+
unmount = possibleUnmount;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return () => {
|
|
49
|
+
if (element && unmount) {
|
|
50
|
+
unmount(element);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}, [ref, mount]);
|
|
54
|
+
return /* @__PURE__ */ jsx2("div", { ref });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/components/Bell.tsx
|
|
58
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
59
|
+
var Bell = React3.memo((props) => {
|
|
60
|
+
const { novuUI, mountElement } = useRendererContext();
|
|
61
|
+
const mount = React3.useCallback(
|
|
62
|
+
(element) => {
|
|
63
|
+
return novuUI.mountComponent({
|
|
64
|
+
name: "Bell",
|
|
65
|
+
element,
|
|
66
|
+
props: props.children ? { mountBell: (el, { unreadCount }) => mountElement(el, props.children?.({ unreadCount })) } : void 0
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
[props.children]
|
|
70
|
+
);
|
|
71
|
+
return /* @__PURE__ */ jsx3(Mounter, { mount });
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// src/components/Inbox.tsx
|
|
75
|
+
import React5 from "react";
|
|
76
|
+
|
|
77
|
+
// src/components/Renderer.tsx
|
|
78
|
+
import { useCallback, useEffect, useState } from "react";
|
|
79
|
+
import ReactDOM from "react-dom";
|
|
80
|
+
import { NovuUI } from "@novu/js/ui";
|
|
81
|
+
|
|
82
|
+
// src/hooks/useDataRef.ts
|
|
83
|
+
import { useRef } from "react";
|
|
84
|
+
var useDataRef = (data) => {
|
|
85
|
+
const ref = useRef(data);
|
|
86
|
+
ref.current = data;
|
|
87
|
+
return ref;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// src/components/Renderer.tsx
|
|
91
|
+
import { jsxs } from "react/jsx-runtime";
|
|
92
|
+
var Renderer = ({ options, children }) => {
|
|
93
|
+
const optionsRef = useDataRef(options);
|
|
94
|
+
const [novuUI, setNovuUI] = useState();
|
|
95
|
+
const [mountedElements, setMountedElements] = useState(/* @__PURE__ */ new Map());
|
|
96
|
+
const mountElement = useCallback(
|
|
97
|
+
(el, mountedElement) => {
|
|
98
|
+
setMountedElements((prev) => {
|
|
99
|
+
const newMountedElements = new Map(prev);
|
|
100
|
+
newMountedElements.set(el, mountedElement);
|
|
101
|
+
return newMountedElements;
|
|
102
|
+
});
|
|
103
|
+
return () => {
|
|
104
|
+
setMountedElements((prev) => {
|
|
105
|
+
const newMountedElements = new Map(prev);
|
|
106
|
+
newMountedElements.delete(el);
|
|
107
|
+
return newMountedElements;
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
[setMountedElements]
|
|
112
|
+
);
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
const novu = new NovuUI(optionsRef.current);
|
|
115
|
+
setNovuUI(novu);
|
|
116
|
+
return () => {
|
|
117
|
+
novu.unmount();
|
|
118
|
+
};
|
|
119
|
+
}, []);
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (!novuUI) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
novuUI.updateAppearance(options.appearance);
|
|
125
|
+
novuUI.updateLocalization(options.localization);
|
|
126
|
+
novuUI.updateTabs(options.tabs);
|
|
127
|
+
novuUI.updateOptions(options.options);
|
|
128
|
+
}, [options]);
|
|
129
|
+
if (!novuUI) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
return /* @__PURE__ */ jsxs(RendererProvider, { value: { mountElement, novuUI }, children: [
|
|
133
|
+
[...mountedElements].map(([element, mountedElement]) => {
|
|
134
|
+
return ReactDOM.createPortal(mountedElement, element);
|
|
135
|
+
}),
|
|
136
|
+
children
|
|
137
|
+
] });
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// src/components/Inbox.tsx
|
|
141
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
142
|
+
var DefaultInbox = ({
|
|
143
|
+
open,
|
|
144
|
+
renderNotification,
|
|
145
|
+
renderBell,
|
|
146
|
+
onNotificationClick,
|
|
147
|
+
onPrimaryActionClick,
|
|
148
|
+
onSecondaryActionClick
|
|
149
|
+
}) => {
|
|
150
|
+
const { novuUI, mountElement } = useRendererContext();
|
|
151
|
+
const mount = React5.useCallback(
|
|
152
|
+
(element) => {
|
|
153
|
+
return novuUI.mountComponent({
|
|
154
|
+
name: "Inbox",
|
|
155
|
+
props: {
|
|
156
|
+
open,
|
|
157
|
+
mountNotification: renderNotification ? (el, { notification }) => mountElement(el, renderNotification({ notification })) : void 0,
|
|
158
|
+
mountBell: renderBell ? (el, { unreadCount }) => mountElement(el, renderBell({ unreadCount })) : void 0,
|
|
159
|
+
onNotificationClick,
|
|
160
|
+
onPrimaryActionClick,
|
|
161
|
+
onSecondaryActionClick
|
|
162
|
+
},
|
|
163
|
+
element
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
[open, renderNotification, renderBell, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick]
|
|
167
|
+
);
|
|
168
|
+
return /* @__PURE__ */ jsx4(Mounter, { mount });
|
|
169
|
+
};
|
|
170
|
+
var Inbox = React5.memo((props) => {
|
|
171
|
+
if (isWithChildrenProps(props)) {
|
|
172
|
+
const { children, ...options2 } = props;
|
|
173
|
+
return /* @__PURE__ */ jsx4(Renderer, { options: options2, children });
|
|
174
|
+
}
|
|
175
|
+
const {
|
|
176
|
+
open,
|
|
177
|
+
renderNotification,
|
|
178
|
+
renderBell,
|
|
179
|
+
onNotificationClick,
|
|
180
|
+
onPrimaryActionClick,
|
|
181
|
+
onSecondaryActionClick,
|
|
182
|
+
...options
|
|
183
|
+
} = props;
|
|
184
|
+
return /* @__PURE__ */ jsx4(Renderer, { options, children: /* @__PURE__ */ jsx4(
|
|
185
|
+
DefaultInbox,
|
|
186
|
+
{
|
|
187
|
+
open,
|
|
188
|
+
renderNotification,
|
|
189
|
+
renderBell,
|
|
190
|
+
onNotificationClick,
|
|
191
|
+
onPrimaryActionClick,
|
|
192
|
+
onSecondaryActionClick
|
|
193
|
+
}
|
|
194
|
+
) });
|
|
195
|
+
});
|
|
196
|
+
function isWithChildrenProps(props) {
|
|
197
|
+
return "children" in props;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/components/Preferences.tsx
|
|
201
|
+
import React6 from "react";
|
|
202
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
203
|
+
var Preferences = () => {
|
|
204
|
+
const { novuUI } = useRendererContext();
|
|
205
|
+
const mount = React6.useCallback((element) => {
|
|
206
|
+
return novuUI.mountComponent({
|
|
207
|
+
name: "Preferences",
|
|
208
|
+
element
|
|
209
|
+
});
|
|
210
|
+
}, []);
|
|
211
|
+
return /* @__PURE__ */ jsx5(Mounter, { mount });
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// src/components/Notifications.tsx
|
|
215
|
+
import React7 from "react";
|
|
216
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
217
|
+
var Notifications = React7.memo(
|
|
218
|
+
({ children, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick }) => {
|
|
219
|
+
const { novuUI, mountElement } = useRendererContext();
|
|
220
|
+
const mount = React7.useCallback(
|
|
221
|
+
(element) => {
|
|
222
|
+
return novuUI.mountComponent({
|
|
223
|
+
name: "Notifications",
|
|
224
|
+
element,
|
|
225
|
+
props: children ? {
|
|
226
|
+
mountNotification: (el, { notification }) => mountElement(el, children?.({ notification })),
|
|
227
|
+
onNotificationClick,
|
|
228
|
+
onPrimaryActionClick,
|
|
229
|
+
onSecondaryActionClick
|
|
230
|
+
} : { onNotificationClick, onPrimaryActionClick, onSecondaryActionClick }
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
[children, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick]
|
|
234
|
+
);
|
|
235
|
+
return /* @__PURE__ */ jsx6(Mounter, { mount });
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
export {
|
|
239
|
+
Bell,
|
|
240
|
+
Inbox,
|
|
241
|
+
Notifications,
|
|
242
|
+
Preferences
|
|
243
|
+
};
|
|
244
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/Bell.tsx","../../src/utils/createContextAndHook.ts","../../src/context/RenderContext.tsx","../../src/components/Mounter.tsx","../../src/components/Inbox.tsx","../../src/components/Renderer.tsx","../../src/hooks/useDataRef.ts","../../src/components/Preferences.tsx","../../src/components/Notifications.tsx"],"sourcesContent":["import React from 'react';\nimport { useRenderer } from '../context/RenderContext';\nimport { Mounter } from './Mounter';\n\nexport type BellRenderProps = ({ unreadCount }: { unreadCount: number }) => React.ReactNode;\n\nexport type BellProps = {\n children?: never | BellRenderProps;\n};\n\nexport const Bell = React.memo((props: BellProps) => {\n const { novuUI, mountElement } = useRenderer();\n\n const mount = React.useCallback(\n (element: HTMLElement) => {\n return novuUI.mountComponent({\n name: 'Bell',\n element,\n props: props.children\n ? { mountBell: (el, { unreadCount }) => mountElement(el, props.children?.({ unreadCount })) }\n : undefined,\n });\n },\n [props.children]\n );\n\n return <Mounter mount={mount} />;\n});\n","import React from 'react';\n\nexport function assertContextExists(contextVal: unknown, msgOrCtx: string | React.Context<any>): asserts contextVal {\n if (!contextVal) {\n throw typeof msgOrCtx === 'string' ? new Error(msgOrCtx) : new Error(`${msgOrCtx.displayName} not found`);\n }\n}\n\ntype Options = { assertCtxFn?: (v: unknown, msg: string) => void };\ntype ContextOf<T> = React.Context<{ value: T } | undefined>;\ntype UseCtxFn<T> = () => T;\n\n/**\n * Creates and returns a Context and two hooks that return the context value.\n * The Context type is derived from the type passed in by the user.\n * The first hook returned guarantees that the context exists so the returned value is always CtxValue\n * The second hook makes no guarantees, so the returned value can be CtxValue | undefined\n */\nexport const createContextAndHook = <CtxVal>(\n displayName: string,\n options?: Options\n): [ContextOf<CtxVal>, UseCtxFn<CtxVal>, UseCtxFn<CtxVal | Partial<CtxVal>>] => {\n const { assertCtxFn = assertContextExists } = options || {};\n const Ctx = React.createContext<{ value: CtxVal } | undefined>(undefined);\n Ctx.displayName = displayName;\n\n const useCtx = () => {\n const ctx = React.useContext(Ctx);\n assertCtxFn(ctx, `Component must be wrapped with the <Inbox /> Component`);\n\n return (ctx as any).value as CtxVal;\n };\n\n const useCtxWithoutGuarantee = () => {\n const ctx = React.useContext(Ctx);\n\n return ctx ? ctx.value : {};\n };\n\n return [Ctx, useCtx, useCtxWithoutGuarantee];\n};\n","import React from 'react';\nimport type { NovuUI } from '@novu/js/ui';\nimport { createContextAndHook } from '../utils/createContextAndHook';\n\nexport type MountedElement = React.ReactNode;\nexport type MountedElements = Map<HTMLElement, MountedElement>;\n\ntype RendererContextValue = {\n mountElement: (el: HTMLElement, mountedElement: MountedElement) => () => void;\n novuUI: NovuUI;\n};\n\nconst [RendererContext, useRendererContext] = createContextAndHook<RendererContextValue>('RendererContext');\n\nconst RendererProvider = (props: React.PropsWithChildren<{ value: RendererContextValue }>) => {\n return <RendererContext.Provider value={{ value: props.value }}>{props.children}</RendererContext.Provider>;\n};\n\nexport { useRendererContext as useRenderer, RendererProvider };\n","import React from 'react';\n\ntype MounterProps = {\n mount: (node: HTMLElement) => ((node: HTMLElement) => void) | void;\n};\n\n/**\n * Mounter allows you to mount a component to a DOM node.\n */\nexport function Mounter({ mount }: MounterProps) {\n const ref = React.useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n let unmount: (node: HTMLDivElement) => void | undefined;\n const element = ref.current;\n if (element && mount) {\n const possibleUnmount = mount(element);\n if (possibleUnmount) {\n unmount = possibleUnmount;\n }\n }\n\n return () => {\n if (element && unmount) {\n unmount(element);\n }\n };\n }, [ref, mount]);\n\n return <div ref={ref} />;\n}\n","import React from 'react';\nimport { useRenderer } from '../context/RenderContext';\nimport { DefaultProps, DefaultInboxProps, WithChildrenProps } from '../utils/types';\nimport { Mounter } from './Mounter';\nimport { Renderer } from './Renderer';\n\nexport type InboxProps = DefaultProps | WithChildrenProps;\n\nconst DefaultInbox = ({\n open,\n renderNotification,\n renderBell,\n onNotificationClick,\n onPrimaryActionClick,\n onSecondaryActionClick,\n}: DefaultInboxProps) => {\n const { novuUI, mountElement } = useRenderer();\n\n const mount = React.useCallback(\n (element: HTMLElement) => {\n return novuUI.mountComponent({\n name: 'Inbox',\n props: {\n open,\n mountNotification: renderNotification\n ? (el, { notification }) => mountElement(el, renderNotification({ notification }))\n : undefined,\n mountBell: renderBell ? (el, { unreadCount }) => mountElement(el, renderBell({ unreadCount })) : undefined,\n onNotificationClick,\n onPrimaryActionClick,\n onSecondaryActionClick,\n },\n element,\n });\n },\n [open, renderNotification, renderBell, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick]\n );\n\n return <Mounter mount={mount} />;\n};\n\nexport const Inbox = React.memo((props: InboxProps) => {\n if (isWithChildrenProps(props)) {\n const { children, ...options } = props;\n\n return <Renderer options={options}>{children}</Renderer>;\n }\n\n const {\n open,\n renderNotification,\n renderBell,\n onNotificationClick,\n onPrimaryActionClick,\n onSecondaryActionClick,\n ...options\n } = props;\n\n return (\n <Renderer options={options}>\n <DefaultInbox\n open={open}\n renderNotification={renderNotification}\n renderBell={renderBell}\n onNotificationClick={onNotificationClick}\n onPrimaryActionClick={onPrimaryActionClick}\n onSecondaryActionClick={onSecondaryActionClick}\n />\n </Renderer>\n );\n});\n\nfunction isWithChildrenProps(props: InboxProps): props is WithChildrenProps {\n return 'children' in props;\n}\n","import React, { useCallback, useEffect, useState } from 'react';\nimport ReactDOM from 'react-dom';\nimport { NovuUI } from '@novu/js/ui';\nimport type { NovuUIOptions } from '@novu/js/ui';\nimport { MountedElement, RendererProvider } from '../context/RenderContext';\nimport { useDataRef } from '../hooks/useDataRef';\n\ntype RendererProps = React.PropsWithChildren<{\n options: NovuUIOptions;\n}>;\n\n/**\n *\n * Renderer component that provides the NovuUI instance and mounts the elements on DOM in a portal\n */\nexport const Renderer = ({ options, children }: RendererProps) => {\n const optionsRef = useDataRef(options);\n const [novuUI, setNovuUI] = useState<NovuUI | undefined>();\n const [mountedElements, setMountedElements] = useState(new Map<HTMLElement, MountedElement>());\n\n const mountElement = useCallback(\n (el: HTMLElement, mountedElement: MountedElement) => {\n setMountedElements((prev) => {\n const newMountedElements = new Map(prev);\n newMountedElements.set(el, mountedElement);\n\n return newMountedElements;\n });\n\n return () => {\n setMountedElements((prev) => {\n const newMountedElements = new Map(prev);\n newMountedElements.delete(el);\n\n return newMountedElements;\n });\n };\n },\n [setMountedElements]\n );\n\n useEffect(() => {\n const novu = new NovuUI(optionsRef.current);\n setNovuUI(novu);\n\n return () => {\n novu.unmount();\n };\n }, []);\n\n useEffect(() => {\n if (!novuUI) {\n return;\n }\n\n novuUI.updateAppearance(options.appearance);\n novuUI.updateLocalization(options.localization);\n novuUI.updateTabs(options.tabs);\n novuUI.updateOptions(options.options);\n }, [options]);\n\n if (!novuUI) {\n return null;\n }\n\n return (\n <RendererProvider value={{ mountElement, novuUI }}>\n {[...mountedElements].map(([element, mountedElement]) => {\n return ReactDOM.createPortal(mountedElement, element);\n })}\n\n {children}\n </RendererProvider>\n );\n};\n","import { useRef } from 'react';\n\nexport const useDataRef = <T>(data: T) => {\n const ref = useRef(data);\n ref.current = data;\n\n return ref;\n};\n","import React from 'react';\nimport { useRenderer } from '../context/RenderContext';\nimport { Mounter } from './Mounter';\n\nexport const Preferences = () => {\n const { novuUI } = useRenderer();\n\n const mount = React.useCallback((element: HTMLElement) => {\n return novuUI.mountComponent({\n name: 'Preferences',\n element,\n });\n }, []);\n\n return <Mounter mount={mount} />;\n};\n","import React from 'react';\nimport { useRenderer } from '../context/RenderContext';\nimport { NotificationsRenderProps } from '../utils/types';\nimport { Mounter } from './Mounter';\nimport type { NotificationClickHandler, NotificationActionClickHandler } from '@novu/js/ui';\n\nexport type NotificationProps = {\n children?: never | NotificationsRenderProps;\n onNotificationClick?: NotificationClickHandler;\n onPrimaryActionClick?: NotificationActionClickHandler;\n onSecondaryActionClick?: NotificationActionClickHandler;\n};\n\nexport const Notifications = React.memo(\n ({ children, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick }: NotificationProps) => {\n const { novuUI, mountElement } = useRenderer();\n\n const mount = React.useCallback(\n (element: HTMLElement) => {\n return novuUI.mountComponent({\n name: 'Notifications',\n element,\n props: children\n ? {\n mountNotification: (el, { notification }) => mountElement(el, children?.({ notification })),\n onNotificationClick,\n onPrimaryActionClick,\n onSecondaryActionClick,\n }\n : { onNotificationClick, onPrimaryActionClick, onSecondaryActionClick },\n });\n },\n [children, onNotificationClick, onPrimaryActionClick, onSecondaryActionClick]\n );\n\n return <Mounter mount={mount} />;\n }\n);\n"],"mappings":"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","names":["React","React","jsx","jsx","React","React","jsx","React","options","React","jsx","React","React","jsx","React"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Notification, NotificationClickHandler, NotificationActionClickHandler, BaseNovuUIOptions } from '@novu/js/ui';
|
|
2
|
+
export { Notification } from '@novu/js/ui';
|
|
3
|
+
|
|
4
|
+
type NotificationsRenderProps = (args: {
|
|
5
|
+
notification: Notification;
|
|
6
|
+
}) => React.ReactNode;
|
|
7
|
+
type DefaultInboxProps = {
|
|
8
|
+
open?: boolean;
|
|
9
|
+
renderNotification?: (args: {
|
|
10
|
+
notification: Notification;
|
|
11
|
+
}) => React.ReactNode;
|
|
12
|
+
renderBell?: ({ unreadCount }: {
|
|
13
|
+
unreadCount: number;
|
|
14
|
+
}) => React.ReactNode;
|
|
15
|
+
onNotificationClick?: NotificationClickHandler;
|
|
16
|
+
onPrimaryActionClick?: NotificationActionClickHandler;
|
|
17
|
+
onSecondaryActionClick?: NotificationActionClickHandler;
|
|
18
|
+
};
|
|
19
|
+
type BaseProps = BaseNovuUIOptions;
|
|
20
|
+
type DefaultProps = BaseProps & DefaultInboxProps & {
|
|
21
|
+
children?: never;
|
|
22
|
+
};
|
|
23
|
+
type WithChildrenProps = BaseProps & {
|
|
24
|
+
children: React.ReactNode;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Exporting all components from the components folder
|
|
29
|
+
* as empty functions to fix build errors in SSR
|
|
30
|
+
* This will be replaced with actual components
|
|
31
|
+
* when we implement the SSR components in @novu/js/ui
|
|
32
|
+
*/
|
|
33
|
+
declare function Inbox(): void;
|
|
34
|
+
declare function Notifications(): void;
|
|
35
|
+
declare function Preferences(): void;
|
|
36
|
+
declare function Bell(): void;
|
|
37
|
+
|
|
38
|
+
export { type BaseProps, Bell, type DefaultInboxProps, type DefaultProps, Inbox, Notifications, type NotificationsRenderProps, Preferences, type WithChildrenProps };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Notification, NotificationClickHandler, NotificationActionClickHandler, BaseNovuUIOptions } from '@novu/js/ui';
|
|
2
|
+
export { Notification } from '@novu/js/ui';
|
|
3
|
+
|
|
4
|
+
type NotificationsRenderProps = (args: {
|
|
5
|
+
notification: Notification;
|
|
6
|
+
}) => React.ReactNode;
|
|
7
|
+
type DefaultInboxProps = {
|
|
8
|
+
open?: boolean;
|
|
9
|
+
renderNotification?: (args: {
|
|
10
|
+
notification: Notification;
|
|
11
|
+
}) => React.ReactNode;
|
|
12
|
+
renderBell?: ({ unreadCount }: {
|
|
13
|
+
unreadCount: number;
|
|
14
|
+
}) => React.ReactNode;
|
|
15
|
+
onNotificationClick?: NotificationClickHandler;
|
|
16
|
+
onPrimaryActionClick?: NotificationActionClickHandler;
|
|
17
|
+
onSecondaryActionClick?: NotificationActionClickHandler;
|
|
18
|
+
};
|
|
19
|
+
type BaseProps = BaseNovuUIOptions;
|
|
20
|
+
type DefaultProps = BaseProps & DefaultInboxProps & {
|
|
21
|
+
children?: never;
|
|
22
|
+
};
|
|
23
|
+
type WithChildrenProps = BaseProps & {
|
|
24
|
+
children: React.ReactNode;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Exporting all components from the components folder
|
|
29
|
+
* as empty functions to fix build errors in SSR
|
|
30
|
+
* This will be replaced with actual components
|
|
31
|
+
* when we implement the SSR components in @novu/js/ui
|
|
32
|
+
*/
|
|
33
|
+
declare function Inbox(): void;
|
|
34
|
+
declare function Notifications(): void;
|
|
35
|
+
declare function Preferences(): void;
|
|
36
|
+
declare function Bell(): void;
|
|
37
|
+
|
|
38
|
+
export { type BaseProps, Bell, type DefaultInboxProps, type DefaultProps, Inbox, Notifications, type NotificationsRenderProps, Preferences, type WithChildrenProps };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/server.ts
|
|
21
|
+
var server_exports = {};
|
|
22
|
+
__export(server_exports, {
|
|
23
|
+
Bell: () => Bell,
|
|
24
|
+
Inbox: () => Inbox,
|
|
25
|
+
Notifications: () => Notifications,
|
|
26
|
+
Preferences: () => Preferences
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(server_exports);
|
|
29
|
+
function Inbox() {
|
|
30
|
+
}
|
|
31
|
+
function Notifications() {
|
|
32
|
+
}
|
|
33
|
+
function Preferences() {
|
|
34
|
+
}
|
|
35
|
+
function Bell() {
|
|
36
|
+
}
|
|
37
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
38
|
+
0 && (module.exports = {
|
|
39
|
+
Bell,
|
|
40
|
+
Inbox,
|
|
41
|
+
Notifications,
|
|
42
|
+
Preferences
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server.ts"],"sourcesContent":["export * from './utils/types';\n/**\n * Exporting all components from the components folder\n * as empty functions to fix build errors in SSR\n * This will be replaced with actual components\n * when we implement the SSR components in @novu/js/ui\n */\nexport function Inbox() {}\nexport function Notifications() {}\nexport function Preferences() {}\nexport function Bell() {}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOO,SAAS,QAAQ;AAAC;AAClB,SAAS,gBAAgB;AAAC;AAC1B,SAAS,cAAc;AAAC;AACxB,SAAS,OAAO;AAAC;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/server.ts"],"sourcesContent":["export * from './utils/types';\n/**\n * Exporting all components from the components folder\n * as empty functions to fix build errors in SSR\n * This will be replaced with actual components\n * when we implement the SSR components in @novu/js/ui\n */\nexport function Inbox() {}\nexport function Notifications() {}\nexport function Preferences() {}\nexport function Bell() {}\n"],"mappings":";AAOO,SAAS,QAAQ;AAAC;AAClB,SAAS,gBAAgB;AAAC;AAC1B,SAAS,cAAc;AAAC;AACxB,SAAS,OAAO;AAAC;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@novu/react",
|
|
3
|
+
"version": "2.0.0-alpha.0",
|
|
4
|
+
"repository": "https://github.com/novuhq/novu",
|
|
5
|
+
"description": "Novu's React SDK for building custom inbox notification experiences",
|
|
6
|
+
"author": "",
|
|
7
|
+
"license": "ISC",
|
|
8
|
+
"main": "dist/server/server.js",
|
|
9
|
+
"module": "dist/client/index.mjs",
|
|
10
|
+
"types": "dist/client/index.d.ts",
|
|
11
|
+
"browser": {
|
|
12
|
+
"./dist/server/server.js": "./dist/client/index.js",
|
|
13
|
+
"./dist/server/server.mjs": "./dist/client/index.mjs"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": {
|
|
18
|
+
"types": "./dist/client/index.d.ts",
|
|
19
|
+
"default": "./dist/server/server.js"
|
|
20
|
+
},
|
|
21
|
+
"require": {
|
|
22
|
+
"types": "./dist/server/server.d.ts",
|
|
23
|
+
"default": "./dist/server/server.js"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"dist/client/**/*",
|
|
30
|
+
"dist/server/**/*"
|
|
31
|
+
],
|
|
32
|
+
"sideEffects": false,
|
|
33
|
+
"private": false,
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build:watch": "tsup --watch",
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"lint": "eslint --ext .ts,.tsx src"
|
|
41
|
+
},
|
|
42
|
+
"browserslist": {
|
|
43
|
+
"production": [
|
|
44
|
+
">0.2%",
|
|
45
|
+
"not dead",
|
|
46
|
+
"not op_mini all"
|
|
47
|
+
],
|
|
48
|
+
"development": [
|
|
49
|
+
"last 1 chrome version",
|
|
50
|
+
"last 1 firefox version",
|
|
51
|
+
"last 1 safari version"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^20.14.12",
|
|
56
|
+
"@types/react": "^18.3.3",
|
|
57
|
+
"@types/react-dom": "^18.3.0",
|
|
58
|
+
"react": "^18.3.1",
|
|
59
|
+
"react-dom": "^18.3.1",
|
|
60
|
+
"tsup": "^8.2.1",
|
|
61
|
+
"typescript": "4.9.5"
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"react": ">=17",
|
|
65
|
+
"react-dom": ">=17"
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@novu/js": "^2.0.0-alpha.0"
|
|
69
|
+
}
|
|
70
|
+
}
|