@ttoss/react-notifications 1.24.72 → 2.1.1

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 CHANGED
@@ -2,25 +2,24 @@
2
2
 
3
3
  ## About
4
4
 
5
- This module handles notifications in your applications and other ttoss modules.
5
+ This module provides a simple way to show notifications in your application using ttoss ecosystem.
6
6
 
7
- ## Getting Started
8
-
9
- ### Install
7
+ ## Installation
10
8
 
11
9
  ```shell
12
- pnpm add @ttoss/notifications @ttoss/ui @ttoss/react-icons @emotion/react
10
+ pnpm add @ttoss/notifications @ttoss/components @ttoss/react-icons @ttoss/ui @emotion/react
13
11
  ```
14
12
 
15
- ## Usage
13
+ ## Getting Started
16
14
 
17
15
  ### Provider
18
16
 
19
- Add a provider on top of your application.
17
+ Add a provider on top of your application and set [Modal app element](https://reactcommunity.org/react-modal/accessibility/).
20
18
 
21
19
  ```tsx
22
20
  import { NotificationsProvider } from '@ttoss/react-notifications';
23
21
  import { ThemeProvider } from "@ttoss/ui";
22
+ import { Modal } from "@ttoss/components";
24
23
 
25
24
  ReactDOM.render(
26
25
  <React.StrictMode>
@@ -31,6 +30,8 @@ ReactDOM.render(
31
30
  </ThemeProvider>
32
31
  </React.StrictMode>,
33
32
  document.getElementById('root')
33
+
34
+ Modal.setAppElement('#root');
34
35
  ```
35
36
 
36
37
  ### Loading
@@ -39,80 +40,139 @@ This modules provides a global loading bar that you can use on every part of you
39
40
 
40
41
  ```tsx
41
42
  import { useNotifications } from '@ttoss/react-notifications';
43
+ import { Box, Button } from '@ttoss/ui';
42
44
 
43
45
  const Component = () => {
44
46
  const { loading, setLoading } = useNotifications();
45
47
 
46
48
  return (
47
- <div>
48
- <button onClick={() => setLoading(true)} disabled={isLoading}>
49
+ <Box>
50
+ <Button onClick={() => setLoading(true)} disabled={isLoading}>
49
51
  Click Me!
50
- </button>
51
- </div>
52
+ </Button>
53
+ </Box>
52
54
  );
53
55
  };
54
56
  ```
55
57
 
56
58
  ### Modal
57
59
 
58
- This modules loads your notification in a Modal. Just put a container under a `NotificationsProvider` and you are ready to go.
60
+ Set `viewType` to `modal` to show a modal notification.
59
61
 
60
62
  ```tsx
61
63
  import { useNotifications } from '@ttoss/react-notifications';
64
+ import { Box, Button } from '@ttoss/ui';
62
65
 
63
66
  const Component = () => {
64
- const { setNotifications } = useNotifications();
67
+ const { addNotification } = useNotifications();
65
68
 
66
69
  return (
67
- <div>
68
- <button
70
+ <Box>
71
+ <Button
69
72
  onClick={() =>
70
- setNotifications({ message: "I'm a notification", type: 'info' })
73
+ addNotification({
74
+ message: "I'm a notification",
75
+ type: 'info',
76
+ viewType: 'modal',
77
+ })
71
78
  }
72
79
  >
73
80
  Click Me!
74
- </button>
75
- <NotificationsModal />
76
- </div>
81
+ </Button>
82
+ </Box>
77
83
  );
78
84
  };
79
85
  ```
80
86
 
81
- ### Resolving notifications
82
-
83
- Additionally, the API accepts a third property in `setNotifications`: `notificationKey`.
84
- In this property, you can insert a placeholder for a particular notification, and, the library gonna resolve any repetated key for this notification to be only one notification.
87
+ ### Toast
85
88
 
86
- In that way, you have more control if you gonna render a repeated notification or not:
89
+ Set `viewType` to `toast` to show a toast notification.
87
90
 
88
91
  ```tsx
89
92
  import { useNotifications } from '@ttoss/react-notifications';
93
+ import { Box, Button } from '@ttoss/ui';
94
+
95
+ const Component = () => {
96
+ const { addNotification } = useNotifications();
97
+
98
+ return (
99
+ <Box>
100
+ <Button
101
+ onClick={() =>
102
+ addNotification({
103
+ message: "I'm a notification",
104
+ type: 'info',
105
+ viewType: 'toast',
106
+ })
107
+ }
108
+ >
109
+ Click Me!
110
+ </Button>
111
+ </Box>
112
+ );
113
+ };
114
+ ```
115
+
116
+ ### NotificationsBox
117
+
118
+ You can use `NotificationsBox` to show the notifications in a specific place. You can render as many `NotificationsBox` as you want in your application.
119
+
120
+ ```tsx
121
+ import { NotificationsBox } from '@ttoss/react-notifications';
122
+ import { Box } from '@ttoss/ui';
90
123
 
91
124
  const Component = () => {
92
- const { setNotifications } = useNotifications();
125
+ return (
126
+ <Box>
127
+ <NotificationsBox />
128
+ </Box>
129
+ );
130
+ };
93
131
 
132
+ const App = () => {
94
133
  return (
95
- <div>
96
- <button
134
+ <Box>
135
+ <NotificationsBox />
136
+ <Component />
137
+ </Box>
138
+ );
139
+ };
140
+ ```
141
+
142
+ In the example above, both `NotificationsBox` will show the notifications.
143
+
144
+ To render the notifications in a specific `NotificationsBox`, you can set the `boxId` in the notification, which is the `id` of the `NotificationsBox` you want to show the notification.
145
+
146
+ ```tsx
147
+ import { useNotifications, NotificationsBox } from '@ttoss/react-notifications';
148
+ import { Box, Button } from '@ttoss/ui';
149
+
150
+ const Component = () => {
151
+ const { addNotification , NotificationsBox } = useNotifications();
152
+
153
+ return (
154
+ <Box>
155
+ <NotificationsBox id="my-box" />
156
+ <Button
97
157
  onClick={() =>
98
- setNotifications([
99
- {
100
- message: "I'm a notification",
101
- type: 'info',
102
- key: 'information',
103
- },
104
- {
105
- message: "I'm considered to be the same notification",
106
- type: 'info',
107
- key: 'information',
108
- },
109
- ])
158
+ addNotification({
159
+ message: "I'm a notification",
160
+ type: 'info',,
161
+ boxId: 'my-box',
162
+ })
110
163
  }
111
164
  >
112
165
  Click Me!
113
- </button>
114
- <NotificationsModal />
115
- </div>
166
+ </Button>
167
+ </Box>
116
168
  );
117
169
  };
118
170
  ```
171
+
172
+ #### Recommendation
173
+
174
+ "You can place the `NotificationsBox` component at the root of your application to handle notifications rendering automatically, eliminating the need to manage it manually elsewhere. If you need a specific `NotificationsBox`, simply render the `NotificationsBox` in the desired location and use the `boxId` property to differentiate it."
175
+
176
+ ## License
177
+
178
+ [MIT](https://github.com/ttoss/ttoss/blob/main/LICENSE)
package/dist/esm/index.js CHANGED
@@ -1,177 +1,204 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
2
 
3
+ // src/NotificationsBox.tsx
4
+ import { NotificationCard } from "@ttoss/components/NotificationCard";
5
+ import { Stack } from "@ttoss/ui";
6
+
3
7
  // src/Provider.tsx
4
- import * as React from "react";
8
+ import { toast, ToastContainer } from "@ttoss/components/Toast";
5
9
  import { Flex, InfiniteLinearProgress } from "@ttoss/ui";
10
+ import * as React from "react";
11
+
12
+ // src/NotificationsModal.tsx
13
+ import { Modal } from "@ttoss/components/Modal";
14
+ import { CloseButton } from "@ttoss/ui";
6
15
  import { jsx, jsxs } from "react/jsx-runtime";
16
+ var NotificationsModal = () => {
17
+ const {
18
+ notifications,
19
+ clearNotifications,
20
+ defaultViewType
21
+ } = useNotifications();
22
+ const modalNotifications = notifications?.filter(notification => {
23
+ if (defaultViewType === "modal" && !notification.viewType) {
24
+ return true;
25
+ }
26
+ return notification.viewType === "modal";
27
+ });
28
+ const isOpen = !!modalNotifications && modalNotifications.length > 0;
29
+ return /* @__PURE__ */jsxs(Modal, {
30
+ isOpen,
31
+ style: {
32
+ content: {
33
+ minWidth: "30%",
34
+ display: "flex",
35
+ flexDirection: "column",
36
+ alignItems: "center",
37
+ gap: "4"
38
+ }
39
+ },
40
+ children: [/* @__PURE__ */jsx(CloseButton, {
41
+ "aria-label": "Close",
42
+ sx: {
43
+ alignSelf: "flex-end"
44
+ },
45
+ onClick: () => {
46
+ clearNotifications();
47
+ }
48
+ }), /* @__PURE__ */jsx(NotificationsBox, {
49
+ notifications: modalNotifications
50
+ })]
51
+ });
52
+ };
53
+
54
+ // src/Provider.tsx
55
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
7
56
  var NotificationsContext = React.createContext({
8
57
  isLoading: false,
9
58
  setLoading: () => {
10
59
  return void 0;
11
60
  },
12
- setNotifications: () => {
61
+ defaultViewType: "box",
62
+ addNotification: () => {
63
+ return void 0;
64
+ },
65
+ removeNotification: () => {
66
+ return void 0;
67
+ },
68
+ clearNotifications: () => {
13
69
  return void 0;
14
70
  }
15
71
  });
16
- var NotificationsProvider = ({
17
- children,
18
- enableNotificationBox
19
- }) => {
72
+ var NotificationsProvider = props => {
20
73
  const [isLoading, setLoading] = React.useState(false);
21
74
  const [notifications, setNotifications] = React.useState();
22
- return /* @__PURE__ */jsxs(NotificationsContext.Provider, {
75
+ const prefix = React.useId();
76
+ const removeNotification = React.useCallback(id => {
77
+ setNotifications(prevNotifications => {
78
+ return prevNotifications?.filter(notification => {
79
+ return notification.id !== id;
80
+ });
81
+ });
82
+ }, []);
83
+ const addNotification = React.useCallback(notification => {
84
+ const newNotifications = (Array.isArray(notification) ? notification : [notification]).map(notification2 => {
85
+ const id = notification2.id || `${prefix}-${Math.random()}`;
86
+ return {
87
+ ...notification2,
88
+ id
89
+ };
90
+ }).filter((notification2, index, notifications2) => {
91
+ return notifications2.findIndex(n => {
92
+ return n.id === notification2.id;
93
+ }) === index;
94
+ });
95
+ const toastNotifications = newNotifications.filter(notification2 => {
96
+ if (notification2.viewType === "toast") {
97
+ return true;
98
+ }
99
+ if (!notification2.viewType && props.defaultViewType === "toast") {
100
+ return true;
101
+ }
102
+ return false;
103
+ });
104
+ toastNotifications.forEach(notification2 => {
105
+ toast(notification2.message, {
106
+ ...notification2.toast,
107
+ type: notification2.type,
108
+ onClose: () => {
109
+ removeNotification(notification2.id);
110
+ notification2.toast?.onClose?.();
111
+ }
112
+ });
113
+ });
114
+ setNotifications((prevNotifications = []) => {
115
+ const oldNotifications = prevNotifications.filter(prevNotification => {
116
+ const newNotification = newNotifications.find(newNotification2 => {
117
+ return newNotification2.id === prevNotification.id;
118
+ });
119
+ if (newNotification) {
120
+ if (newNotification.viewType === "toast") {
121
+ return false;
122
+ }
123
+ if (!newNotification.viewType && props.defaultViewType === "toast") {
124
+ return false;
125
+ }
126
+ }
127
+ return true;
128
+ });
129
+ return [...oldNotifications, ...newNotifications];
130
+ });
131
+ }, [prefix, props.defaultViewType, removeNotification]);
132
+ const clearNotifications = React.useCallback(() => {
133
+ setNotifications(void 0);
134
+ }, []);
135
+ return /* @__PURE__ */jsxs2(NotificationsContext.Provider, {
23
136
  value: {
24
137
  isLoading,
25
138
  setLoading,
26
- setNotifications,
27
139
  notifications,
28
- enableNotificationBox
140
+ addNotification,
141
+ removeNotification,
142
+ clearNotifications,
143
+ defaultViewType: props.defaultViewType || "box"
29
144
  },
30
- children: [isLoading && /* @__PURE__ */jsx(Flex, {
145
+ children: [/* @__PURE__ */jsx2(ToastContainer, {
146
+ ...props.toast
147
+ }), /* @__PURE__ */jsx2(NotificationsModal, {}), isLoading && /* @__PURE__ */jsx2(Flex, {
31
148
  sx: {
32
149
  position: "absolute",
33
150
  width: "100%",
34
151
  top: 0
35
152
  },
36
- children: /* @__PURE__ */jsx(InfiniteLinearProgress, {})
37
- }), children]
153
+ children: /* @__PURE__ */jsx2(InfiniteLinearProgress, {})
154
+ }), props.children]
38
155
  });
39
156
  };
40
157
  var useNotifications = () => {
41
- const {
42
- isLoading,
43
- setLoading,
44
- notifications,
45
- setNotifications
46
- } = React.useContext(NotificationsContext);
47
- return {
48
- isLoading,
49
- setLoading,
50
- notifications,
51
- setNotifications
52
- };
158
+ return React.useContext(NotificationsContext);
53
159
  };
54
160
 
55
161
  // src/NotificationsBox.tsx
56
- import * as React2 from "react";
57
- import { Button, Flex as Flex2, Stack } from "@ttoss/ui";
58
- import { jsx as jsx2 } from "react/jsx-runtime";
59
- var NotificationBoxWrapper = ({
60
- direction,
61
- notifications,
62
- children
63
- }) => {
64
- const sx = {
65
- alignItems: "center",
66
- justifyContent: "center",
67
- gap: "md",
68
- marginTop: !notifications || Array.isArray(notifications) && !notifications.length ? 0 : "2xl"
69
- };
70
- return direction === "row" ? /* @__PURE__ */jsx2(Flex2, {
71
- sx,
72
- children
73
- }) : /* @__PURE__ */jsx2(Stack, {
74
- sx,
75
- children
76
- });
77
- };
78
- var resolveNotifications = notifications => {
79
- if (!Array.isArray(notifications)) return notifications;
80
- const keyedNotifications = new Map(notifications.filter(notification => {
81
- return notification.key;
82
- }).map(notification => {
83
- return [notification?.key, notification];
84
- })).values();
85
- const nonKeyedNotifications = notifications.filter(notification => {
86
- return !notification.key;
87
- }).map((notification, index) => {
88
- return {
89
- ...notification,
90
- key: index.toString()
91
- };
92
- });
93
- return Array.from(keyedNotifications).concat(nonKeyedNotifications);
94
- };
95
- var NotificationsBox = ({
96
- direction = "row"
97
- }) => {
162
+ import { jsx as jsx3 } from "react/jsx-runtime";
163
+ var NotificationsBox = props => {
98
164
  const {
99
- setNotifications,
100
- notifications
165
+ notifications,
166
+ removeNotification,
167
+ defaultViewType
101
168
  } = useNotifications();
102
- if (!notifications) {
103
- return null;
104
- }
105
- const renderNotifications = resolveNotifications(notifications);
106
- const ButtonMemoized = React2.memo(({
107
- notification: {
108
- key,
109
- type,
110
- message
169
+ const boxNotifications = props.notifications || notifications?.filter(notification => {
170
+ if (notification.viewType !== "box" && defaultViewType !== "box") {
171
+ return false;
111
172
  }
112
- }) => {
113
- return /* @__PURE__ */jsx2(Button, {
114
- sx: {
115
- backgroundColor: type === "error" ? "danger" : "positive"
116
- },
117
- onClick: () => {
118
- if (Array.isArray(renderNotifications) && renderNotifications.length > 1) {
119
- return setNotifications(renderNotifications.filter(notification => {
120
- return notification.key !== key;
121
- }));
122
- }
123
- return setNotifications(void 0);
124
- },
125
- rightIcon: "close",
126
- leftIcon: type === "error" ? "warning" : void 0,
127
- children: message
128
- });
173
+ if (!props.id && notification.boxId) {
174
+ return false;
175
+ }
176
+ if (!props.id && !notification.boxId) {
177
+ return true;
178
+ }
179
+ return notification.boxId === props.id;
129
180
  });
130
- ButtonMemoized.displayName = "ButtonMemoized";
131
- return /* @__PURE__ */jsx2(NotificationBoxWrapper, {
132
- ...{
133
- notifications,
134
- direction
181
+ const hasBoxNotifications = Array.isArray(boxNotifications);
182
+ if (!hasBoxNotifications) {
183
+ return null;
184
+ }
185
+ return /* @__PURE__ */jsx3(Stack, {
186
+ sx: {
187
+ width: "full",
188
+ gap: "1"
135
189
  },
136
- children: Array.isArray(renderNotifications) ? renderNotifications.map(notification => {
137
- return /* @__PURE__ */jsx2(ButtonMemoized, {
138
- notification
139
- }, notification.key);
140
- }) : /* @__PURE__ */jsx2(ButtonMemoized, {
141
- notification: renderNotifications
190
+ children: boxNotifications.map(notification => {
191
+ return /* @__PURE__ */jsx3(NotificationCard, {
192
+ type: notification.type,
193
+ title: notification.title,
194
+ message: notification.message,
195
+ onClose: () => {
196
+ if (notification.id) {
197
+ removeNotification(notification.id);
198
+ }
199
+ }
200
+ }, notification.id);
142
201
  })
143
202
  });
144
203
  };
145
-
146
- // src/NotificationsModal.tsx
147
- import { CloseButton } from "@ttoss/ui";
148
- import { Modal } from "@ttoss/components/Modal";
149
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
150
- var NotificationsModal = () => {
151
- const {
152
- notifications,
153
- setNotifications
154
- } = useNotifications();
155
- return /* @__PURE__ */jsxs2(Modal, {
156
- isOpen: !!notifications,
157
- style: {
158
- content: {
159
- minWidth: "30%",
160
- display: "flex",
161
- flexDirection: "column",
162
- alignItems: "center"
163
- }
164
- },
165
- children: [/* @__PURE__ */jsx3(CloseButton, {
166
- sx: {
167
- alignSelf: "flex-end"
168
- },
169
- onClick: () => {
170
- setNotifications(void 0);
171
- }
172
- }), /* @__PURE__ */jsx3(NotificationsBox, {
173
- direction: "column"
174
- })]
175
- });
176
- };
177
- export { NotificationsBox, NotificationsModal, NotificationsProvider, useNotifications };
204
+ export { NotificationsBox, NotificationsProvider, toast, useNotifications };
package/dist/index.d.ts CHANGED
@@ -1,27 +1,37 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ToastOptions, ToastContainerProps } from '@ttoss/components/Toast';
3
+ export { toast } from '@ttoss/components/Toast';
2
4
  import * as React from 'react';
3
5
 
4
- type NotifyParams = {
5
- key?: string;
6
- message: 'string' | React.ReactNode;
7
- type: 'success' | 'error' | 'warning' | 'info';
6
+ type ViewType = 'toast' | 'modal' | 'box';
7
+ type Notification = {
8
+ id?: string | number;
9
+ title?: string;
10
+ message: string;
11
+ type: 'warning' | 'error' | 'info' | 'success';
12
+ viewType?: ViewType;
13
+ toast?: ToastOptions;
14
+ boxId?: string | number;
8
15
  };
9
16
  type NotificationsProviderProps = {
10
17
  children: React.ReactNode;
11
- enableNotificationBox?: boolean;
18
+ defaultViewType?: ViewType;
19
+ toast?: ToastContainerProps;
12
20
  };
13
- declare const NotificationsProvider: ({ children, enableNotificationBox, }: NotificationsProviderProps) => react_jsx_runtime.JSX.Element;
21
+ declare const NotificationsProvider: (props: NotificationsProviderProps) => react_jsx_runtime.JSX.Element;
14
22
  declare const useNotifications: () => {
15
23
  isLoading: boolean;
16
24
  setLoading: (arg: boolean) => void;
17
- notifications: NotifyParams | NotifyParams[] | undefined;
18
- setNotifications: (args: NotifyParams | NotifyParams[] | undefined) => void;
25
+ defaultViewType: ViewType;
26
+ notifications?: Notification[];
27
+ addNotification(notification: Notification | Notification[]): void;
28
+ removeNotification(id: string | number): void;
29
+ clearNotifications(): void;
19
30
  };
20
31
 
21
- declare const NotificationsBox: ({ direction, }: {
22
- direction?: "row" | "column";
32
+ declare const NotificationsBox: (props: {
33
+ id?: string | number;
34
+ notifications?: Notification[] | undefined;
23
35
  }) => react_jsx_runtime.JSX.Element | null;
24
36
 
25
- declare const NotificationsModal: () => react_jsx_runtime.JSX.Element;
26
-
27
- export { NotificationsBox, NotificationsModal, NotificationsProvider, type NotificationsProviderProps, type NotifyParams, useNotifications };
37
+ export { type Notification, NotificationsBox, NotificationsProvider, type NotificationsProviderProps, useNotifications };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/react-notifications",
3
- "version": "1.24.72",
3
+ "version": "2.1.1",
4
4
  "description": "ttoss notifications module for React apps.",
5
5
  "license": "MIT",
6
6
  "author": "ttoss",
@@ -25,8 +25,8 @@
25
25
  "sideEffects": false,
26
26
  "peerDependencies": {
27
27
  "react": ">=16.8.0",
28
- "@ttoss/components": "^2.0.24",
29
- "@ttoss/ui": "^5.1.6",
28
+ "@ttoss/components": "^2.1.1",
29
+ "@ttoss/ui": "^5.2.0",
30
30
  "@ttoss/react-icons": "^0.4.9"
31
31
  },
32
32
  "devDependencies": {
@@ -34,11 +34,11 @@
34
34
  "jest": "^29.7.0",
35
35
  "react": "^19.0.0",
36
36
  "tsup": "^8.3.5",
37
- "@ttoss/components": "^2.0.24",
37
+ "@ttoss/components": "^2.1.1",
38
38
  "@ttoss/config": "^1.35.2",
39
- "@ttoss/react-icons": "^0.4.9",
40
39
  "@ttoss/test-utils": "^2.1.22",
41
- "@ttoss/ui": "^5.1.6"
40
+ "@ttoss/ui": "^5.2.0",
41
+ "@ttoss/react-icons": "^0.4.9"
42
42
  },
43
43
  "keywords": [
44
44
  "React",