@monetr/notify 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/dist/style.css ADDED
@@ -0,0 +1,351 @@
1
+ :where([data-monetr-notifier-stack], [data-monetr-notification]) {
2
+ --monetr-notification-bg: #1c1c1ee0;
3
+ --monetr-notification-fg: #e8e8e8;
4
+ --monetr-notification-fg-muted: #e8e8e88c;
5
+ --monetr-notification-border: #ffffff14;
6
+ --monetr-notification-radius: 14px;
7
+ --monetr-notification-blur: 16px;
8
+ --monetr-notification-shadow: 0 4px 12px #0000001f, 0 1px 3px #0000000f;
9
+ --monetr-notification-success: #34d399;
10
+ --monetr-notification-error: #f87171;
11
+ --monetr-notification-warning: #fbbf24;
12
+ --monetr-notification-info: #60a5fa;
13
+ --monetr-notification-progress-track: #ffffff14;
14
+ --monetr-notification-z-index: 9999;
15
+ --monetr-notification-stack-edge-padding: 16px;
16
+ --monetr-notification-width: min(380px, calc(100vw - 32px));
17
+ }
18
+
19
+ [data-monetr-notifier-stack] {
20
+ z-index: var(--monetr-notification-z-index);
21
+ pointer-events: none;
22
+ width: var(--monetr-notification-width);
23
+ font-family: system-ui, -apple-system, Segoe UI, sans-serif;
24
+ position: fixed;
25
+ }
26
+
27
+ [data-monetr-notifier-stack][data-stack-anchor^="bottom-"] {
28
+ bottom: max(env(safe-area-inset-bottom, 0px), var(--monetr-notification-stack-edge-padding));
29
+ }
30
+
31
+ [data-monetr-notifier-stack][data-stack-anchor^="top-"] {
32
+ top: max(env(safe-area-inset-top, 0px), var(--monetr-notification-stack-edge-padding));
33
+ }
34
+
35
+ [data-monetr-notifier-stack][data-stack-anchor$="-left"] {
36
+ left: var(--monetr-notification-stack-edge-padding);
37
+ }
38
+
39
+ [data-monetr-notifier-stack][data-stack-anchor$="-right"] {
40
+ right: var(--monetr-notification-stack-edge-padding);
41
+ }
42
+
43
+ [data-monetr-notifier-stack][data-stack-anchor$="-center"] {
44
+ left: 50%;
45
+ transform: translateX(-50%);
46
+ }
47
+
48
+ [data-monetr-notifier-stack][data-stack-empty="true"] {
49
+ display: none;
50
+ }
51
+
52
+ [data-monetr-notification] {
53
+ pointer-events: auto;
54
+ box-sizing: border-box;
55
+ background: var(--monetr-notification-bg);
56
+ width: 100%;
57
+ color: var(--monetr-notification-fg);
58
+ border: 1px solid var(--monetr-notification-border);
59
+ border-radius: var(--monetr-notification-radius);
60
+ box-shadow: var(--monetr-notification-shadow);
61
+ backdrop-filter: blur(var(--monetr-notification-blur));
62
+ -webkit-user-select: none;
63
+ user-select: none;
64
+ letter-spacing: -.01em;
65
+ touch-action: none;
66
+ --monetr-stack-index: 0;
67
+ --monetr-stack-offset: 8px;
68
+ --monetr-stack-scale: calc(1 - var(--monetr-stack-index) * .05);
69
+ --monetr-stack-direction: -1;
70
+ --monetr-drag-x: 0px;
71
+ --monetr-drag-y: 0px;
72
+ --monetr-entry-y: 0px;
73
+ --monetr-notification-progress: 0;
74
+ --monetr-notification-progress-bar: var(--monetr-notification-fg-muted);
75
+ transform: translate3d(var(--monetr-drag-x),
76
+ calc(var(--monetr-stack-index) * var(--monetr-stack-direction) * var(--monetr-stack-offset) +
77
+ var(--monetr-drag-y) +
78
+ var(--monetr-entry-y)),
79
+ 0)
80
+ scale(var(--monetr-stack-scale));
81
+ z-index: calc(1000 - var(--monetr-stack-index));
82
+ opacity: 1;
83
+ align-items: center;
84
+ gap: 12px;
85
+ padding: 14px 16px 14px 18px;
86
+ font-size: 14px;
87
+ font-weight: 500;
88
+ line-height: 1.45;
89
+ transition: transform .4s cubic-bezier(.175, .885, .32, 1.275), opacity .3s ease-out;
90
+ display: flex;
91
+ position: absolute;
92
+ left: 0;
93
+ right: 0;
94
+ overflow: hidden;
95
+ }
96
+
97
+ [data-monetr-notification][data-stack-index="0"] {
98
+ position: relative;
99
+ }
100
+
101
+ [data-monetr-notifier-stack][data-stack-anchor^="bottom-"] [data-monetr-notification] {
102
+ bottom: 0;
103
+ }
104
+
105
+ [data-monetr-notifier-stack][data-stack-anchor^="top-"] [data-monetr-notification] {
106
+ --monetr-stack-direction: 1;
107
+ top: 0;
108
+ }
109
+
110
+ [data-monetr-notification][data-stack-index="3"], [data-monetr-notification][data-stack-index="4"], [data-monetr-notification][data-stack-index="5"], [data-monetr-notification][data-stack-index="6"], [data-monetr-notification][data-stack-index="7"] {
111
+ opacity: 0;
112
+ }
113
+
114
+ [data-monetr-notification]:not([data-stack-index="0"]) {
115
+ pointer-events: none;
116
+ }
117
+
118
+ [data-monetr-notifier-stack][data-stack-hovered="true"] [data-monetr-notification] {
119
+ --monetr-stack-offset: 14px;
120
+ --monetr-stack-scale: calc(1 - var(--monetr-stack-index) * .02);
121
+ }
122
+
123
+ [data-monetr-notification][data-dragging="true"] {
124
+ transition: opacity .15s ease-out;
125
+ }
126
+
127
+ [data-monetr-notification][data-state="entering"] {
128
+ opacity: 0;
129
+ --monetr-entry-y: 24px;
130
+ }
131
+
132
+ [data-monetr-notifier-stack][data-stack-anchor^="top-"] [data-monetr-notification][data-state="entering"] {
133
+ --monetr-entry-y: -24px;
134
+ }
135
+
136
+ [data-monetr-notification][data-state="exiting"] {
137
+ pointer-events: none;
138
+ animation: .28s ease-out forwards monetr-notification-toast-out;
139
+ }
140
+
141
+ [data-monetr-notifier-stack][data-stack-anchor^="top-"] [data-monetr-notification][data-state="exiting"] {
142
+ animation-name: monetr-notification-toast-out-top;
143
+ }
144
+
145
+ [data-monetr-notification][data-state="swiped-out"][data-swipe-direction="1"] {
146
+ pointer-events: none;
147
+ animation: .3s cubic-bezier(.32, .72, .37, .92) forwards monetr-notification-swipe-out-right;
148
+ }
149
+
150
+ [data-monetr-notification][data-state="swiped-out"][data-swipe-direction="-1"] {
151
+ pointer-events: none;
152
+ animation: .3s cubic-bezier(.32, .72, .37, .92) forwards monetr-notification-swipe-out-left;
153
+ }
154
+
155
+ [data-monetr-notification][data-monetr-notification-variant="success"] {
156
+ --monetr-notification-progress-bar: var(--monetr-notification-success);
157
+ }
158
+
159
+ [data-monetr-notification][data-monetr-notification-variant="error"] {
160
+ --monetr-notification-progress-bar: var(--monetr-notification-error);
161
+ }
162
+
163
+ [data-monetr-notification][data-monetr-notification-variant="warning"] {
164
+ --monetr-notification-progress-bar: var(--monetr-notification-warning);
165
+ }
166
+
167
+ [data-monetr-notification][data-monetr-notification-variant="info"] {
168
+ --monetr-notification-progress-bar: var(--monetr-notification-info);
169
+ }
170
+
171
+ [data-monetr-notification-icon-slot] {
172
+ flex-shrink: 0;
173
+ justify-content: center;
174
+ align-items: center;
175
+ display: inline-flex;
176
+ }
177
+
178
+ [data-monetr-notification-pip] {
179
+ background: var(--monetr-notification-progress-bar);
180
+ width: 8px;
181
+ height: 8px;
182
+ box-shadow: 0 0 8px color-mix(in srgb, var(--monetr-notification-progress-bar) 53%, transparent);
183
+ border-radius: 50%;
184
+ flex-shrink: 0;
185
+ animation: .4s cubic-bezier(.34, 1.56, .64, 1) .15s both monetr-notification-pip-in;
186
+ }
187
+
188
+ [data-monetr-notification-message] {
189
+ flex: auto;
190
+ min-width: 0;
191
+ }
192
+
193
+ [data-monetr-notification-action] {
194
+ border: 1px solid var(--monetr-notification-border);
195
+ color: var(--monetr-notification-fg);
196
+ font: inherit;
197
+ cursor: pointer;
198
+ background: none;
199
+ border-radius: 8px;
200
+ flex-shrink: 0;
201
+ padding: 5px 12px;
202
+ font-size: 12px;
203
+ font-weight: 600;
204
+ transition: background .15s, border-color .15s;
205
+ }
206
+
207
+ [data-monetr-notification-action]:hover {
208
+ background: var(--monetr-notification-border);
209
+ }
210
+
211
+ [data-monetr-notification-dismiss] {
212
+ color: var(--monetr-notification-fg-muted);
213
+ cursor: pointer;
214
+ background: none;
215
+ border: none;
216
+ border-radius: 6px;
217
+ flex-shrink: 0;
218
+ padding: 2px 4px;
219
+ font-size: 16px;
220
+ line-height: 1;
221
+ transition: color .15s;
222
+ }
223
+
224
+ [data-monetr-notification-dismiss]:hover {
225
+ color: var(--monetr-notification-fg);
226
+ }
227
+
228
+ [data-monetr-notification-progress-track] {
229
+ background: var(--monetr-notification-progress-track);
230
+ pointer-events: none;
231
+ height: 2px;
232
+ position: absolute;
233
+ bottom: 0;
234
+ left: 0;
235
+ right: 0;
236
+ }
237
+
238
+ [data-monetr-notification-progress-bar-fill] {
239
+ transform-origin: 0;
240
+ width: 100%;
241
+ height: 100%;
242
+ transform: scaleX(calc(1 - var(--monetr-notification-progress, 0)));
243
+ background: var(--monetr-notification-progress-bar);
244
+ opacity: .7;
245
+ border-radius: 0 1px 1px 0;
246
+ transition: transform 80ms linear;
247
+ }
248
+
249
+ [data-monetr-notification][data-stack-hovered-self="true"] [data-monetr-notification-progress-bar-fill] {
250
+ transition: none;
251
+ }
252
+
253
+ @keyframes monetr-notification-pip-in {
254
+ 0% {
255
+ opacity: 0;
256
+ transform: scale(0);
257
+ }
258
+
259
+ 100% {
260
+ opacity: 1;
261
+ transform: scale(1);
262
+ }
263
+ }
264
+
265
+ @keyframes monetr-notification-toast-out {
266
+ 0% {
267
+ opacity: 1;
268
+ transform: translate3d(0, 0, 0) scale(1);
269
+ }
270
+
271
+ 100% {
272
+ opacity: 0;
273
+ transform: translate3d(0, 12px, 0) scale(.94);
274
+ }
275
+ }
276
+
277
+ @keyframes monetr-notification-toast-out-top {
278
+ 0% {
279
+ opacity: 1;
280
+ transform: translate3d(0, 0, 0) scale(1);
281
+ }
282
+
283
+ 100% {
284
+ opacity: 0;
285
+ transform: translate3d(0, -12px, 0) scale(.94);
286
+ }
287
+ }
288
+
289
+ @keyframes monetr-notification-swipe-out-right {
290
+ 0% {
291
+ opacity: 1;
292
+ transform: translate3d(var(--monetr-drag-x, 0px), 0, 0) scale(1);
293
+ }
294
+
295
+ 100% {
296
+ opacity: 0;
297
+ transform: translate3d(120%, 0, 0) scale(.9) rotate(8deg);
298
+ }
299
+ }
300
+
301
+ @keyframes monetr-notification-swipe-out-left {
302
+ 0% {
303
+ opacity: 1;
304
+ transform: translate3d(var(--monetr-drag-x, 0px), 0, 0) scale(1);
305
+ }
306
+
307
+ 100% {
308
+ opacity: 0;
309
+ transform: translate3d(-120%, 0, 0) scale(.9) rotate(-8deg);
310
+ }
311
+ }
312
+
313
+ [data-monetr-notification][data-state="swiped-out"][data-swipe-axis="y"][data-swipe-direction="1"] {
314
+ animation: .3s cubic-bezier(.32, .72, .37, .92) forwards monetr-notification-swipe-out-down;
315
+ }
316
+
317
+ [data-monetr-notification][data-state="swiped-out"][data-swipe-axis="y"][data-swipe-direction="-1"] {
318
+ animation: .3s cubic-bezier(.32, .72, .37, .92) forwards monetr-notification-swipe-out-up;
319
+ }
320
+
321
+ @keyframes monetr-notification-swipe-out-down {
322
+ 0% {
323
+ opacity: 1;
324
+ transform: translate3d(0, var(--monetr-drag-y, 0px), 0) scale(1);
325
+ }
326
+
327
+ 100% {
328
+ opacity: 0;
329
+ transform: translate3d(0, 120%, 0) scale(.9);
330
+ }
331
+ }
332
+
333
+ @keyframes monetr-notification-swipe-out-up {
334
+ 0% {
335
+ opacity: 1;
336
+ transform: translate3d(0, var(--monetr-drag-y, 0px), 0) scale(1);
337
+ }
338
+
339
+ 100% {
340
+ opacity: 0;
341
+ transform: translate3d(0, -120%, 0) scale(.9);
342
+ }
343
+ }
344
+
345
+ @media (prefers-reduced-motion: reduce) {
346
+ [data-monetr-notification], [data-monetr-notification-progress-bar-fill], [data-monetr-notification-pip] {
347
+ transition: none !important;
348
+ animation: none !important;
349
+ }
350
+ }
351
+
@@ -0,0 +1,79 @@
1
+ import type { ReactNode, SyntheticEvent } from 'react';
2
+ export type VariantType = 'default' | 'success' | 'error' | 'warning' | 'info';
3
+ export type SnackbarKey = string | number;
4
+ export type SnackbarMessage = ReactNode;
5
+ export interface AnchorOrigin {
6
+ vertical: 'top' | 'bottom';
7
+ horizontal: 'left' | 'center' | 'right';
8
+ }
9
+ export type CloseReason = 'timeout' | 'instructed' | 'maxsnack' | 'clickaway' | 'swipe';
10
+ export interface OptionsObject {
11
+ key?: SnackbarKey;
12
+ variant?: VariantType;
13
+ /** Shorthand for `autoHideDuration: null`. */
14
+ persist?: boolean;
15
+ /** Auto-hide duration in ms. `null` or `0` disables auto-hide; `undefined` inherits the provider default. */
16
+ autoHideDuration?: number | null;
17
+ anchorOrigin?: AnchorOrigin;
18
+ /** When `true`, this item ignores window-blur events. */
19
+ disableWindowBlurListener?: boolean;
20
+ /** Action button. A node, or a render fn that receives the snackbar key. */
21
+ action?: ReactNode | ((key: SnackbarKey) => ReactNode);
22
+ onClose?: (event: SyntheticEvent<HTMLElement> | null, reason: CloseReason, key: SnackbarKey) => void;
23
+ }
24
+ export interface ProviderContext {
25
+ enqueueSnackbar: (message: SnackbarMessage, options?: OptionsObject) => SnackbarKey;
26
+ /** Pass no argument to close every active notification. */
27
+ closeSnackbar: (key?: SnackbarKey) => void;
28
+ }
29
+ export interface SnackbarProviderProps {
30
+ children: ReactNode;
31
+ /** Visible items per anchor stack. */
32
+ maxSnack?: number;
33
+ /** Default anchor for new notifications. */
34
+ anchorOrigin?: AnchorOrigin;
35
+ /** Default auto-hide duration in ms. `null` disables auto-hide. */
36
+ autoHideDuration?: number | null;
37
+ /** Replaces the variant pip with a node per variant. Rendered before the message. */
38
+ iconVariant?: Partial<Record<VariantType, ReactNode>>;
39
+ /** Portal target. Defaults to `document.body`. */
40
+ container?: HTMLElement | null;
41
+ /** When the lazy renderer chunk should download. Defaults to `'idle'`. */
42
+ prefetch?: 'never' | 'idle' | 'mount';
43
+ }
44
+ export type SnackbarItemState = 'entering' | 'visible' | 'paused' | 'exiting' | 'swiped-out';
45
+ export interface SnackbarItem {
46
+ key: SnackbarKey;
47
+ message: SnackbarMessage;
48
+ variant: VariantType;
49
+ autoHideDuration: number | null;
50
+ anchorOrigin: AnchorOrigin;
51
+ disableWindowBlurListener: boolean;
52
+ action: OptionsObject['action'];
53
+ onClose: OptionsObject['onClose'];
54
+ createdAt: number;
55
+ state: SnackbarItemState;
56
+ swipeDirection: -1 | 0 | 1;
57
+ }
58
+ export interface NotifierRootProps {
59
+ queue: SnackbarItem[];
60
+ dispatch: (action: NotifierAction) => void;
61
+ iconVariant?: Partial<Record<VariantType, ReactNode>>;
62
+ container?: HTMLElement | null;
63
+ maxSnack: number;
64
+ }
65
+ export type NotifierAction = {
66
+ type: 'set-state';
67
+ key: SnackbarKey;
68
+ state: SnackbarItemState;
69
+ } | {
70
+ type: 'swipe';
71
+ key: SnackbarKey;
72
+ direction: -1 | 1;
73
+ } | {
74
+ type: 'dismiss';
75
+ key: SnackbarKey;
76
+ } | {
77
+ type: 'remove';
78
+ key: SnackbarKey;
79
+ };
package/dist/types.js ADDED
File without changes
@@ -0,0 +1,29 @@
1
+ import type { AnchorOrigin, NotifierAction, OptionsObject, SnackbarItem, SnackbarKey, SnackbarMessage } from './types';
2
+ type EnqueueAction = {
3
+ type: 'enqueue';
4
+ item: SnackbarItem;
5
+ dropKeys: SnackbarKey[];
6
+ };
7
+ export type Action = EnqueueAction | NotifierAction | {
8
+ type: 'close-all';
9
+ };
10
+ export declare function anchorKey(a: AnchorOrigin): string;
11
+ export declare function isLiveState(state: SnackbarItem['state']): boolean;
12
+ interface ProviderDefaults {
13
+ anchorOrigin?: AnchorOrigin;
14
+ autoHideDuration?: number | null;
15
+ }
16
+ export declare function makeInstance(message: SnackbarMessage, options: OptionsObject | undefined, defaults: ProviderDefaults): SnackbarItem;
17
+ export declare function findOverflowKeys(state: SnackbarItem[], incoming: SnackbarItem, maxSnack: number): SnackbarKey[];
18
+ export declare function queueReducer(state: SnackbarItem[], action: Action): SnackbarItem[];
19
+ export declare const initialQueue: SnackbarItem[];
20
+ export declare function providerDefaults(props: {
21
+ anchorOrigin?: AnchorOrigin;
22
+ autoHideDuration?: number | null;
23
+ maxSnack?: number;
24
+ }): {
25
+ anchorOrigin: AnchorOrigin;
26
+ autoHideDuration: number | null;
27
+ maxSnack: number;
28
+ };
29
+ export {};
@@ -0,0 +1,97 @@
1
+ import { DEFAULT_ANCHOR_HORIZONTAL, DEFAULT_ANCHOR_VERTICAL, DEFAULT_AUTO_HIDE_DURATION, DEFAULT_MAX_SNACK } from "./constants.js";
2
+ let _idCounter = 0;
3
+ function nextKey() {
4
+ _idCounter += 1;
5
+ return _idCounter;
6
+ }
7
+ function sameAnchor(a, b) {
8
+ return a.vertical === b.vertical && a.horizontal === b.horizontal;
9
+ }
10
+ function anchorKey(a) {
11
+ return `${a.vertical}-${a.horizontal}`;
12
+ }
13
+ function isLiveState(state) {
14
+ return 'entering' === state || 'visible' === state || 'paused' === state;
15
+ }
16
+ function resolveAutoHideDuration(options, defaults) {
17
+ if (options?.persist === true) return null;
18
+ if (options?.autoHideDuration !== void 0) return options.autoHideDuration;
19
+ if (void 0 !== defaults.autoHideDuration) return defaults.autoHideDuration;
20
+ return DEFAULT_AUTO_HIDE_DURATION;
21
+ }
22
+ function makeInstance(message, options, defaults) {
23
+ const variant = options?.variant ?? 'default';
24
+ const anchorOrigin = options?.anchorOrigin ?? defaults.anchorOrigin ?? {
25
+ vertical: DEFAULT_ANCHOR_VERTICAL,
26
+ horizontal: DEFAULT_ANCHOR_HORIZONTAL
27
+ };
28
+ return {
29
+ key: options?.key ?? nextKey(),
30
+ message,
31
+ variant,
32
+ autoHideDuration: resolveAutoHideDuration(options, defaults),
33
+ anchorOrigin,
34
+ disableWindowBlurListener: options?.disableWindowBlurListener ?? false,
35
+ action: options?.action,
36
+ onClose: options?.onClose,
37
+ createdAt: Date.now(),
38
+ state: 'entering',
39
+ swipeDirection: 0
40
+ };
41
+ }
42
+ function findOverflowKeys(state, incoming, maxSnack) {
43
+ const live = state.filter((s)=>sameAnchor(s.anchorOrigin, incoming.anchorOrigin) && isLiveState(s.state));
44
+ const projected = live.length + 1;
45
+ if (projected <= maxSnack) return [];
46
+ const dropCount = projected - maxSnack;
47
+ return live.slice(-dropCount).map((s)=>s.key);
48
+ }
49
+ function queueReducer(state, action) {
50
+ switch(action.type){
51
+ case 'enqueue':
52
+ {
53
+ const filtered = action.dropKeys.length > 0 ? state.filter((s)=>!action.dropKeys.includes(s.key)) : state;
54
+ return [
55
+ action.item,
56
+ ...filtered
57
+ ];
58
+ }
59
+ case 'set-state':
60
+ return state.map((s)=>s.key === action.key ? {
61
+ ...s,
62
+ state: action.state
63
+ } : s);
64
+ case 'swipe':
65
+ return state.map((s)=>s.key === action.key ? {
66
+ ...s,
67
+ state: 'swiped-out',
68
+ swipeDirection: action.direction
69
+ } : s);
70
+ case 'dismiss':
71
+ return state.map((s)=>s.key === action.key && isLiveState(s.state) ? {
72
+ ...s,
73
+ state: 'exiting'
74
+ } : s);
75
+ case 'remove':
76
+ return state.filter((s)=>s.key !== action.key);
77
+ case 'close-all':
78
+ return state.map((s)=>isLiveState(s.state) ? {
79
+ ...s,
80
+ state: 'exiting'
81
+ } : s);
82
+ default:
83
+ return state;
84
+ }
85
+ }
86
+ const initialQueue = [];
87
+ function providerDefaults(props) {
88
+ return {
89
+ anchorOrigin: props.anchorOrigin ?? {
90
+ vertical: DEFAULT_ANCHOR_VERTICAL,
91
+ horizontal: DEFAULT_ANCHOR_HORIZONTAL
92
+ },
93
+ autoHideDuration: void 0 !== props.autoHideDuration ? props.autoHideDuration : DEFAULT_AUTO_HIDE_DURATION,
94
+ maxSnack: props.maxSnack ?? DEFAULT_MAX_SNACK
95
+ };
96
+ }
97
+ export { anchorKey, findOverflowKeys, initialQueue, isLiveState, makeInstance, providerDefaults, queueReducer };
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@monetr/notify",
3
+ "version": "1.0.0",
4
+ "description": "Notification/toast component for React.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "sideEffects": [
12
+ "**/*.css"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "keywords": [
21
+ "react",
22
+ "toast",
23
+ "notification",
24
+ "snackbar",
25
+ "alert"
26
+ ],
27
+ "publishConfig": {
28
+ "registry": "https://registry.npmjs.org",
29
+ "access": "public"
30
+ },
31
+ "license": "EPL-2.0",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/monetr/notify.git"
35
+ },
36
+ "bugs": {
37
+ "url": "https://github.com/monetr/notify/issues"
38
+ },
39
+ "devDependencies": {
40
+ "@biomejs/biome": "2.4.14",
41
+ "@rsbuild/core": "2.0.4",
42
+ "@rsbuild/plugin-react": "2.0.0",
43
+ "@rsdoctor/rspack-plugin": "1.5.9",
44
+ "@rslib/core": "0.21.3",
45
+ "@rspress/core": "2.0.10",
46
+ "@rstest/adapter-rslib": "0.2.2",
47
+ "@rstest/browser": "0.9.10",
48
+ "@rstest/browser-react": "0.9.10",
49
+ "@rstest/core": "0.9.10",
50
+ "@types/node": "24.12.2",
51
+ "@types/react": "18.3.28",
52
+ "@types/react-dom": "18.3.7",
53
+ "playwright": "1.59.1",
54
+ "react": "18.3.1",
55
+ "react-dom": "18.3.1",
56
+ "typescript": "6.0.3"
57
+ },
58
+ "peerDependencies": {
59
+ "react": "^18.0 || ^19.0.0 || ^19.0.0-rc",
60
+ "react-dom": "^18.0 || ^19.0.0 || ^19.0.0-rc"
61
+ },
62
+ "scripts": {
63
+ "type-check": "tsc --noEmit",
64
+ "build": "pnpm type-check && rslib build",
65
+ "build:analyze": "RSDOCTOR=true pnpm build",
66
+ "clean": "rm -rf node_modules dist",
67
+ "dev": "rslib build --watch",
68
+ "dev:demo": "rsbuild dev",
69
+ "docs:dev": "rspress dev",
70
+ "docs:build": "rspress build",
71
+ "docs:preview": "rspress preview",
72
+ "lint": "biome check",
73
+ "lint:fix": "biome check --write",
74
+ "test": "rstest run",
75
+ "test:firefox": "rstest run --browser.name firefox",
76
+ "test:webkit": "rstest run --browser.name webkit"
77
+ }
78
+ }