@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/LICENSE +277 -0
- package/README.md +53 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +11 -0
- package/dist/context.d.ts +3 -0
- package/dist/context.js +8 -0
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +4 -0
- package/dist/hook.d.ts +1 -0
- package/dist/hook.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/notification.d.ts +11 -0
- package/dist/notification.js +343 -0
- package/dist/notifier.d.ts +10 -0
- package/dist/notifier.js +28 -0
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +122 -0
- package/dist/renderer.d.ts +4 -0
- package/dist/renderer.js +72 -0
- package/dist/style.css +351 -0
- package/dist/types.d.ts +79 -0
- package/dist/types.js +0 -0
- package/dist/use-queue.d.ts +29 -0
- package/dist/use-queue.js +97 -0
- package/package.json +78 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { DRAG_DAMPEN_MAX, DRAG_DISMISS_DISTANCE, DRAG_VELOCITY_THRESHOLD, DRAG_VELOCITY_WINDOW_MS, EXIT_TRANSITION_MS, SWIPE_EXIT_MS } from "./constants.js";
|
|
4
|
+
import { dampenValue } from "./helpers.js";
|
|
5
|
+
const initialDrag = {
|
|
6
|
+
startX: 0,
|
|
7
|
+
startY: 0,
|
|
8
|
+
currentX: 0,
|
|
9
|
+
currentY: 0,
|
|
10
|
+
dragging: false,
|
|
11
|
+
sampleAt: 0,
|
|
12
|
+
sampleX: 0,
|
|
13
|
+
sampleY: 0
|
|
14
|
+
};
|
|
15
|
+
function isDismissAllowed(anchor, axis, sign) {
|
|
16
|
+
if ('y' === axis) return sign === ('top' === anchor.vertical ? -1 : 1);
|
|
17
|
+
if ('left' === anchor.horizontal) return -1 === sign;
|
|
18
|
+
if ('right' === anchor.horizontal) return 1 === sign;
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
const PIP_VARIANTS = [
|
|
22
|
+
'success',
|
|
23
|
+
'error',
|
|
24
|
+
'warning',
|
|
25
|
+
'info'
|
|
26
|
+
];
|
|
27
|
+
function Notification(props) {
|
|
28
|
+
const { instance, index, stackHovered, iconNode, dispatch } = props;
|
|
29
|
+
const { key, anchorOrigin, autoHideDuration, disableWindowBlurListener, action, variant, message, state, swipeDirection } = instance;
|
|
30
|
+
const elementRef = useRef(null);
|
|
31
|
+
const dragRef = useRef({
|
|
32
|
+
...initialDrag
|
|
33
|
+
});
|
|
34
|
+
const elapsedRef = useRef(0);
|
|
35
|
+
const lastResumeRef = useRef(null);
|
|
36
|
+
const swipeAxisRef = useRef(null);
|
|
37
|
+
const onCloseRef = useRef(instance.onClose);
|
|
38
|
+
useEffect(()=>{
|
|
39
|
+
onCloseRef.current = instance.onClose;
|
|
40
|
+
});
|
|
41
|
+
const [dragOffset, setDragOffset] = useState({
|
|
42
|
+
x: 0,
|
|
43
|
+
y: 0
|
|
44
|
+
});
|
|
45
|
+
const [windowBlurred, setWindowBlurred] = useState(false);
|
|
46
|
+
useEffect(()=>{
|
|
47
|
+
if ('entering' !== state) return;
|
|
48
|
+
let raf2 = 0;
|
|
49
|
+
const raf1 = requestAnimationFrame(()=>{
|
|
50
|
+
raf2 = requestAnimationFrame(()=>{
|
|
51
|
+
dispatch({
|
|
52
|
+
type: 'set-state',
|
|
53
|
+
key,
|
|
54
|
+
state: 'visible'
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
return ()=>{
|
|
59
|
+
cancelAnimationFrame(raf1);
|
|
60
|
+
cancelAnimationFrame(raf2);
|
|
61
|
+
};
|
|
62
|
+
}, [
|
|
63
|
+
state,
|
|
64
|
+
key,
|
|
65
|
+
dispatch
|
|
66
|
+
]);
|
|
67
|
+
useEffect(()=>{
|
|
68
|
+
if (disableWindowBlurListener) return;
|
|
69
|
+
const onBlur = ()=>setWindowBlurred(true);
|
|
70
|
+
const onFocus = ()=>setWindowBlurred(false);
|
|
71
|
+
window.addEventListener('blur', onBlur);
|
|
72
|
+
window.addEventListener('focus', onFocus);
|
|
73
|
+
return ()=>{
|
|
74
|
+
window.removeEventListener('blur', onBlur);
|
|
75
|
+
window.removeEventListener('focus', onFocus);
|
|
76
|
+
};
|
|
77
|
+
}, [
|
|
78
|
+
disableWindowBlurListener
|
|
79
|
+
]);
|
|
80
|
+
const effectivelyPaused = 'paused' === state || stackHovered || windowBlurred;
|
|
81
|
+
useEffect(()=>{
|
|
82
|
+
if ('visible' !== state || null === autoHideDuration || effectivelyPaused) {
|
|
83
|
+
if (null !== lastResumeRef.current) {
|
|
84
|
+
elapsedRef.current += Date.now() - lastResumeRef.current;
|
|
85
|
+
lastResumeRef.current = null;
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
lastResumeRef.current = Date.now();
|
|
90
|
+
const remaining = Math.max(0, autoHideDuration - elapsedRef.current);
|
|
91
|
+
const timeoutId = window.setTimeout(()=>{
|
|
92
|
+
onCloseRef.current?.(null, 'timeout', key);
|
|
93
|
+
dispatch({
|
|
94
|
+
type: 'dismiss',
|
|
95
|
+
key
|
|
96
|
+
});
|
|
97
|
+
}, remaining);
|
|
98
|
+
return ()=>{
|
|
99
|
+
window.clearTimeout(timeoutId);
|
|
100
|
+
if (null !== lastResumeRef.current) {
|
|
101
|
+
elapsedRef.current += Date.now() - lastResumeRef.current;
|
|
102
|
+
lastResumeRef.current = null;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}, [
|
|
106
|
+
state,
|
|
107
|
+
autoHideDuration,
|
|
108
|
+
effectivelyPaused,
|
|
109
|
+
key,
|
|
110
|
+
dispatch
|
|
111
|
+
]);
|
|
112
|
+
useEffect(()=>{
|
|
113
|
+
if ('visible' !== state || null === autoHideDuration || effectivelyPaused) return;
|
|
114
|
+
let raf = 0;
|
|
115
|
+
const tick = ()=>{
|
|
116
|
+
const el = elementRef.current;
|
|
117
|
+
if (!el) return;
|
|
118
|
+
const live = null !== lastResumeRef.current ? Date.now() - lastResumeRef.current : 0;
|
|
119
|
+
const total = elapsedRef.current + live;
|
|
120
|
+
const progress = Math.min(1, total / autoHideDuration);
|
|
121
|
+
el.style.setProperty('--monetr-notification-progress', String(progress));
|
|
122
|
+
if (progress < 1 && null !== lastResumeRef.current) raf = requestAnimationFrame(tick);
|
|
123
|
+
};
|
|
124
|
+
raf = requestAnimationFrame(tick);
|
|
125
|
+
return ()=>cancelAnimationFrame(raf);
|
|
126
|
+
}, [
|
|
127
|
+
state,
|
|
128
|
+
autoHideDuration,
|
|
129
|
+
effectivelyPaused
|
|
130
|
+
]);
|
|
131
|
+
useEffect(()=>{
|
|
132
|
+
if ('exiting' !== state && 'swiped-out' !== state) return;
|
|
133
|
+
const fallback = window.setTimeout(()=>dispatch({
|
|
134
|
+
type: 'remove',
|
|
135
|
+
key
|
|
136
|
+
}), 'swiped-out' === state ? SWIPE_EXIT_MS + 100 : EXIT_TRANSITION_MS + 100);
|
|
137
|
+
return ()=>window.clearTimeout(fallback);
|
|
138
|
+
}, [
|
|
139
|
+
state,
|
|
140
|
+
key,
|
|
141
|
+
dispatch
|
|
142
|
+
]);
|
|
143
|
+
const handlePointerDown = useCallback((event)=>{
|
|
144
|
+
if (0 !== index) return;
|
|
145
|
+
if ('visible' !== state && 'entering' !== state && 'paused' !== state) return;
|
|
146
|
+
const target = event.target;
|
|
147
|
+
if (target?.closest('button, a, input, textarea, select, [role="button"]')) return;
|
|
148
|
+
const now = performance.now();
|
|
149
|
+
dragRef.current = {
|
|
150
|
+
startX: event.clientX,
|
|
151
|
+
startY: event.clientY,
|
|
152
|
+
currentX: event.clientX,
|
|
153
|
+
currentY: event.clientY,
|
|
154
|
+
dragging: true,
|
|
155
|
+
sampleAt: now,
|
|
156
|
+
sampleX: event.clientX,
|
|
157
|
+
sampleY: event.clientY
|
|
158
|
+
};
|
|
159
|
+
try {
|
|
160
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
161
|
+
} catch {}
|
|
162
|
+
}, [
|
|
163
|
+
index,
|
|
164
|
+
state
|
|
165
|
+
]);
|
|
166
|
+
const handlePointerMove = useCallback((event)=>{
|
|
167
|
+
const drag = dragRef.current;
|
|
168
|
+
if (!drag.dragging) return;
|
|
169
|
+
drag.currentX = event.clientX;
|
|
170
|
+
drag.currentY = event.clientY;
|
|
171
|
+
const now = performance.now();
|
|
172
|
+
if (now - drag.sampleAt > DRAG_VELOCITY_WINDOW_MS) {
|
|
173
|
+
drag.sampleAt = now;
|
|
174
|
+
drag.sampleX = event.clientX;
|
|
175
|
+
drag.sampleY = event.clientY;
|
|
176
|
+
}
|
|
177
|
+
const dx = event.clientX - drag.startX;
|
|
178
|
+
const dy = event.clientY - drag.startY;
|
|
179
|
+
const ax = Math.abs(dx);
|
|
180
|
+
const ay = Math.abs(dy);
|
|
181
|
+
if (ax >= ay) {
|
|
182
|
+
const sign = Math.sign(dx) || 1;
|
|
183
|
+
if (isDismissAllowed(anchorOrigin, 'x', sign)) setDragOffset({
|
|
184
|
+
x: dx,
|
|
185
|
+
y: 0
|
|
186
|
+
});
|
|
187
|
+
else {
|
|
188
|
+
const damp = Math.min(DRAG_DAMPEN_MAX, Math.max(0, dampenValue(ax)));
|
|
189
|
+
setDragOffset({
|
|
190
|
+
x: damp * sign,
|
|
191
|
+
y: 0
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
const sign = Math.sign(dy) || 1;
|
|
196
|
+
if (isDismissAllowed(anchorOrigin, 'y', sign)) setDragOffset({
|
|
197
|
+
x: 0,
|
|
198
|
+
y: dy
|
|
199
|
+
});
|
|
200
|
+
else {
|
|
201
|
+
const damp = Math.min(DRAG_DAMPEN_MAX, Math.max(0, dampenValue(ay)));
|
|
202
|
+
setDragOffset({
|
|
203
|
+
x: 0,
|
|
204
|
+
y: damp * sign
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}, [
|
|
209
|
+
anchorOrigin
|
|
210
|
+
]);
|
|
211
|
+
const handlePointerUp = useCallback(()=>{
|
|
212
|
+
const drag = dragRef.current;
|
|
213
|
+
if (!drag.dragging) return;
|
|
214
|
+
drag.dragging = false;
|
|
215
|
+
const dx = drag.currentX - drag.startX;
|
|
216
|
+
const dy = drag.currentY - drag.startY;
|
|
217
|
+
const ax = Math.abs(dx);
|
|
218
|
+
const ay = Math.abs(dy);
|
|
219
|
+
const elapsed = Math.max(1, performance.now() - drag.sampleAt);
|
|
220
|
+
const vx = Math.abs(drag.currentX - drag.sampleX) / elapsed;
|
|
221
|
+
const vy = Math.abs(drag.currentY - drag.sampleY) / elapsed;
|
|
222
|
+
const checkAxis = (axis, delta, vel)=>{
|
|
223
|
+
const sign = Math.sign(delta) || 1;
|
|
224
|
+
if (!isDismissAllowed(anchorOrigin, axis, sign)) return null;
|
|
225
|
+
if (Math.abs(delta) >= DRAG_DISMISS_DISTANCE || vel >= DRAG_VELOCITY_THRESHOLD) return {
|
|
226
|
+
axis,
|
|
227
|
+
direction: sign
|
|
228
|
+
};
|
|
229
|
+
return null;
|
|
230
|
+
};
|
|
231
|
+
const outcome = ax >= ay ? checkAxis('x', dx, vx) ?? checkAxis('y', dy, vy) : checkAxis('y', dy, vy) ?? checkAxis('x', dx, vx);
|
|
232
|
+
if (outcome) {
|
|
233
|
+
swipeAxisRef.current = outcome.axis;
|
|
234
|
+
onCloseRef.current?.(null, 'swipe', key);
|
|
235
|
+
dispatch({
|
|
236
|
+
type: 'swipe',
|
|
237
|
+
key,
|
|
238
|
+
direction: outcome.direction
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
setDragOffset({
|
|
243
|
+
x: 0,
|
|
244
|
+
y: 0
|
|
245
|
+
});
|
|
246
|
+
}, [
|
|
247
|
+
anchorOrigin,
|
|
248
|
+
key,
|
|
249
|
+
dispatch
|
|
250
|
+
]);
|
|
251
|
+
const handlePointerCancel = useCallback(()=>{
|
|
252
|
+
dragRef.current.dragging = false;
|
|
253
|
+
setDragOffset({
|
|
254
|
+
x: 0,
|
|
255
|
+
y: 0
|
|
256
|
+
});
|
|
257
|
+
}, []);
|
|
258
|
+
const handleDismiss = useCallback((event)=>{
|
|
259
|
+
event.stopPropagation();
|
|
260
|
+
onCloseRef.current?.(event, 'instructed', key);
|
|
261
|
+
dispatch({
|
|
262
|
+
type: 'dismiss',
|
|
263
|
+
key
|
|
264
|
+
});
|
|
265
|
+
}, [
|
|
266
|
+
key,
|
|
267
|
+
dispatch
|
|
268
|
+
]);
|
|
269
|
+
const handleAction = useCallback((event)=>{
|
|
270
|
+
event.stopPropagation();
|
|
271
|
+
onCloseRef.current?.(event, 'instructed', key);
|
|
272
|
+
dispatch({
|
|
273
|
+
type: 'dismiss',
|
|
274
|
+
key
|
|
275
|
+
});
|
|
276
|
+
}, [
|
|
277
|
+
key,
|
|
278
|
+
dispatch
|
|
279
|
+
]);
|
|
280
|
+
const renderedAction = null == action ? null : 'function' == typeof action ? action(key) : action;
|
|
281
|
+
const cssVars = {
|
|
282
|
+
'--monetr-stack-index': String(index),
|
|
283
|
+
'--monetr-drag-x': `${dragOffset.x}px`,
|
|
284
|
+
'--monetr-drag-y': `${dragOffset.y}px`
|
|
285
|
+
};
|
|
286
|
+
const showPip = !iconNode && PIP_VARIANTS.includes(variant);
|
|
287
|
+
return /*#__PURE__*/ jsxs("li", {
|
|
288
|
+
"aria-atomic": "true",
|
|
289
|
+
"aria-live": 'error' === variant || 'warning' === variant ? 'assertive' : 'polite',
|
|
290
|
+
"data-dragging": dragRef.current.dragging ? 'true' : 'false',
|
|
291
|
+
"data-monetr-notification": "",
|
|
292
|
+
"data-monetr-notification-anchor": `${anchorOrigin.vertical}-${anchorOrigin.horizontal}`,
|
|
293
|
+
"data-monetr-notification-variant": variant,
|
|
294
|
+
"data-stack-hovered-self": stackHovered ? 'true' : 'false',
|
|
295
|
+
"data-stack-index": index,
|
|
296
|
+
"data-state": state,
|
|
297
|
+
"data-swipe-axis": swipeAxisRef.current ?? void 0,
|
|
298
|
+
"data-swipe-direction": 0 !== swipeDirection ? String(swipeDirection) : void 0,
|
|
299
|
+
onPointerCancel: handlePointerCancel,
|
|
300
|
+
onPointerDown: handlePointerDown,
|
|
301
|
+
onPointerMove: handlePointerMove,
|
|
302
|
+
onPointerUp: handlePointerUp,
|
|
303
|
+
ref: elementRef,
|
|
304
|
+
role: 'error' === variant || 'warning' === variant ? 'alert' : 'status',
|
|
305
|
+
style: cssVars,
|
|
306
|
+
children: [
|
|
307
|
+
iconNode ? /*#__PURE__*/ jsx("span", {
|
|
308
|
+
"data-monetr-notification-icon-slot": "",
|
|
309
|
+
children: iconNode
|
|
310
|
+
}) : null,
|
|
311
|
+
showPip ? /*#__PURE__*/ jsx("span", {
|
|
312
|
+
"aria-hidden": "true",
|
|
313
|
+
"data-monetr-notification-pip": ""
|
|
314
|
+
}) : null,
|
|
315
|
+
/*#__PURE__*/ jsx("span", {
|
|
316
|
+
"data-monetr-notification-message": "",
|
|
317
|
+
children: message
|
|
318
|
+
}),
|
|
319
|
+
renderedAction ? /*#__PURE__*/ jsx("button", {
|
|
320
|
+
"data-monetr-notification-action": "",
|
|
321
|
+
onClick: handleAction,
|
|
322
|
+
type: "button",
|
|
323
|
+
children: renderedAction
|
|
324
|
+
}) : null,
|
|
325
|
+
/*#__PURE__*/ jsx("button", {
|
|
326
|
+
"aria-label": "Dismiss",
|
|
327
|
+
"data-monetr-notification-dismiss": "",
|
|
328
|
+
"data-testid": 0 === index ? 'notification-dismiss' : void 0,
|
|
329
|
+
onClick: handleDismiss,
|
|
330
|
+
type: "button",
|
|
331
|
+
children: "\xd7"
|
|
332
|
+
}),
|
|
333
|
+
null !== autoHideDuration ? /*#__PURE__*/ jsx("div", {
|
|
334
|
+
"aria-hidden": "true",
|
|
335
|
+
"data-monetr-notification-progress-track": "",
|
|
336
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
337
|
+
"data-monetr-notification-progress-bar-fill": ""
|
|
338
|
+
})
|
|
339
|
+
}) : null
|
|
340
|
+
]
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
export { Notification };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { AnchorOrigin, NotifierAction, SnackbarItem, VariantType } from './types';
|
|
3
|
+
interface NotifierProps {
|
|
4
|
+
anchor: AnchorOrigin;
|
|
5
|
+
instances: SnackbarItem[];
|
|
6
|
+
iconVariant?: Partial<Record<VariantType, ReactNode>>;
|
|
7
|
+
dispatch: (action: NotifierAction) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function Notifier(props: NotifierProps): JSX.Element;
|
|
10
|
+
export {};
|
package/dist/notifier.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Notification } from "./notification.js";
|
|
4
|
+
function Notifier(props) {
|
|
5
|
+
const { anchor, instances, iconVariant, dispatch } = props;
|
|
6
|
+
const [hovered, setHovered] = useState(false);
|
|
7
|
+
return /*#__PURE__*/ jsx("ol", {
|
|
8
|
+
"data-monetr-notifier-stack": "",
|
|
9
|
+
"data-stack-anchor": `${anchor.vertical}-${anchor.horizontal}`,
|
|
10
|
+
"data-stack-empty": 0 === instances.length ? 'true' : 'false',
|
|
11
|
+
"data-stack-hovered": hovered ? 'true' : 'false',
|
|
12
|
+
onPointerEnter: ()=>setHovered(true),
|
|
13
|
+
onPointerLeave: ()=>setHovered(false),
|
|
14
|
+
style: {
|
|
15
|
+
listStyle: 'none',
|
|
16
|
+
padding: 0,
|
|
17
|
+
margin: 0
|
|
18
|
+
},
|
|
19
|
+
children: instances.map((inst, index)=>/*#__PURE__*/ jsx(Notification, {
|
|
20
|
+
dispatch: dispatch,
|
|
21
|
+
iconNode: iconVariant?.[inst.variant],
|
|
22
|
+
index: index,
|
|
23
|
+
instance: inst,
|
|
24
|
+
stackHovered: hovered
|
|
25
|
+
}, inst.key))
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export { Notifier };
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { SnackbarContext } from "./context.js";
|
|
4
|
+
import { findOverflowKeys, initialQueue, isLiveState, makeInstance, providerDefaults, queueReducer } from "./use-queue.js";
|
|
5
|
+
const LazyNotifierRoot = /*#__PURE__*/ lazy(()=>import("./renderer.js"));
|
|
6
|
+
function SnackbarProvider(props) {
|
|
7
|
+
const { children, prefetch = 'idle', iconVariant, container, anchorOrigin, autoHideDuration, maxSnack } = props;
|
|
8
|
+
const defaults = useMemo(()=>providerDefaults({
|
|
9
|
+
anchorOrigin,
|
|
10
|
+
autoHideDuration,
|
|
11
|
+
maxSnack
|
|
12
|
+
}), [
|
|
13
|
+
anchorOrigin?.vertical,
|
|
14
|
+
anchorOrigin?.horizontal,
|
|
15
|
+
anchorOrigin,
|
|
16
|
+
autoHideDuration,
|
|
17
|
+
maxSnack
|
|
18
|
+
]);
|
|
19
|
+
const queueRef = useRef(initialQueue);
|
|
20
|
+
const [, setVersion] = useState(0);
|
|
21
|
+
const dispatch = useCallback((action)=>{
|
|
22
|
+
queueRef.current = queueReducer(queueRef.current, action);
|
|
23
|
+
setVersion((v)=>v + 1);
|
|
24
|
+
}, []);
|
|
25
|
+
const prefetchedRef = useRef(false);
|
|
26
|
+
const [active, setActiveState] = useState(false);
|
|
27
|
+
const activeRef = useRef(false);
|
|
28
|
+
const setActive = useCallback(()=>{
|
|
29
|
+
if (!activeRef.current) {
|
|
30
|
+
activeRef.current = true;
|
|
31
|
+
setActiveState(true);
|
|
32
|
+
}
|
|
33
|
+
}, []);
|
|
34
|
+
const enqueueSnackbar = useCallback((message, options)=>{
|
|
35
|
+
if (options?.key !== void 0) {
|
|
36
|
+
const existing = queueRef.current.find((i)=>i.key === options.key);
|
|
37
|
+
if (existing) return existing.key;
|
|
38
|
+
}
|
|
39
|
+
const item = makeInstance(message, options, defaults);
|
|
40
|
+
const dropKeys = findOverflowKeys(queueRef.current, item, defaults.maxSnack);
|
|
41
|
+
for (const key of dropKeys){
|
|
42
|
+
const dropped = queueRef.current.find((i)=>i.key === key);
|
|
43
|
+
dropped?.onClose?.(null, 'maxsnack', key);
|
|
44
|
+
}
|
|
45
|
+
dispatch({
|
|
46
|
+
type: 'enqueue',
|
|
47
|
+
item,
|
|
48
|
+
dropKeys
|
|
49
|
+
});
|
|
50
|
+
setActive();
|
|
51
|
+
return item.key;
|
|
52
|
+
}, [
|
|
53
|
+
defaults,
|
|
54
|
+
setActive,
|
|
55
|
+
dispatch
|
|
56
|
+
]);
|
|
57
|
+
const closeSnackbar = useCallback((key)=>{
|
|
58
|
+
if (void 0 === key) {
|
|
59
|
+
for (const item of queueRef.current)if (isLiveState(item.state)) item.onClose?.(null, 'instructed', item.key);
|
|
60
|
+
dispatch({
|
|
61
|
+
type: 'close-all'
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const item = queueRef.current.find((i)=>i.key === key);
|
|
66
|
+
if (item && isLiveState(item.state)) item.onClose?.(null, 'instructed', key);
|
|
67
|
+
dispatch({
|
|
68
|
+
type: 'dismiss',
|
|
69
|
+
key
|
|
70
|
+
});
|
|
71
|
+
}, [
|
|
72
|
+
dispatch
|
|
73
|
+
]);
|
|
74
|
+
const api = useMemo(()=>({
|
|
75
|
+
enqueueSnackbar,
|
|
76
|
+
closeSnackbar
|
|
77
|
+
}), [
|
|
78
|
+
enqueueSnackbar,
|
|
79
|
+
closeSnackbar
|
|
80
|
+
]);
|
|
81
|
+
useEffect(()=>{
|
|
82
|
+
if ('never' === prefetch || "u" < typeof window || prefetchedRef.current) return;
|
|
83
|
+
const run = ()=>{
|
|
84
|
+
prefetchedRef.current = true;
|
|
85
|
+
import("./renderer.js");
|
|
86
|
+
};
|
|
87
|
+
if ('mount' === prefetch) return void run();
|
|
88
|
+
const w = window;
|
|
89
|
+
const ric = w.requestIdleCallback;
|
|
90
|
+
const cic = w.cancelIdleCallback;
|
|
91
|
+
if (ric) {
|
|
92
|
+
const id = ric(run);
|
|
93
|
+
return ()=>{
|
|
94
|
+
if (cic) cic(id);
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const id = window.setTimeout(run, 200);
|
|
98
|
+
return ()=>window.clearTimeout(id);
|
|
99
|
+
}, [
|
|
100
|
+
prefetch
|
|
101
|
+
]);
|
|
102
|
+
const rendererDispatch = useCallback((a)=>dispatch(a), [
|
|
103
|
+
dispatch
|
|
104
|
+
]);
|
|
105
|
+
return /*#__PURE__*/ jsxs(SnackbarContext.Provider, {
|
|
106
|
+
value: api,
|
|
107
|
+
children: [
|
|
108
|
+
children,
|
|
109
|
+
active ? /*#__PURE__*/ jsx(Suspense, {
|
|
110
|
+
fallback: null,
|
|
111
|
+
children: /*#__PURE__*/ jsx(LazyNotifierRoot, {
|
|
112
|
+
container: container,
|
|
113
|
+
dispatch: rendererDispatch,
|
|
114
|
+
iconVariant: iconVariant,
|
|
115
|
+
maxSnack: defaults.maxSnack,
|
|
116
|
+
queue: queueRef.current
|
|
117
|
+
})
|
|
118
|
+
}) : null
|
|
119
|
+
]
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
export { SnackbarProvider };
|
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import { Notifier } from "./notifier.js";
|
|
5
|
+
import { anchorKey } from "./use-queue.js";
|
|
6
|
+
import "./style.css";
|
|
7
|
+
const ALL_ANCHORS = [
|
|
8
|
+
{
|
|
9
|
+
vertical: 'top',
|
|
10
|
+
horizontal: 'left'
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
vertical: 'top',
|
|
14
|
+
horizontal: 'center'
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
vertical: 'top',
|
|
18
|
+
horizontal: 'right'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
vertical: 'bottom',
|
|
22
|
+
horizontal: 'left'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
vertical: 'bottom',
|
|
26
|
+
horizontal: 'center'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
vertical: 'bottom',
|
|
30
|
+
horizontal: 'right'
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
function NotifierRoot(props) {
|
|
34
|
+
const { queue, dispatch, iconVariant, container } = props;
|
|
35
|
+
const [target, setTarget] = useState(null);
|
|
36
|
+
useEffect(()=>{
|
|
37
|
+
if (container) setTarget(container);
|
|
38
|
+
else if ("u" > typeof document) setTarget(document.body);
|
|
39
|
+
}, [
|
|
40
|
+
container
|
|
41
|
+
]);
|
|
42
|
+
const grouped = useMemo(()=>{
|
|
43
|
+
const map = new Map();
|
|
44
|
+
for (const item of queue){
|
|
45
|
+
const k = anchorKey(item.anchorOrigin);
|
|
46
|
+
const list = map.get(k);
|
|
47
|
+
if (list) list.push(item);
|
|
48
|
+
else map.set(k, [
|
|
49
|
+
item
|
|
50
|
+
]);
|
|
51
|
+
}
|
|
52
|
+
return map;
|
|
53
|
+
}, [
|
|
54
|
+
queue
|
|
55
|
+
]);
|
|
56
|
+
if (!target) return null;
|
|
57
|
+
return /*#__PURE__*/ jsx(Fragment, {
|
|
58
|
+
children: ALL_ANCHORS.map((anchor)=>{
|
|
59
|
+
const k = anchorKey(anchor);
|
|
60
|
+
const items = grouped.get(k) ?? [];
|
|
61
|
+
if (0 === items.length) return null;
|
|
62
|
+
return /*#__PURE__*/ createPortal(/*#__PURE__*/ jsx(Notifier, {
|
|
63
|
+
anchor: anchor,
|
|
64
|
+
dispatch: dispatch,
|
|
65
|
+
iconVariant: iconVariant,
|
|
66
|
+
instances: items
|
|
67
|
+
}), target, k);
|
|
68
|
+
})
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const renderer = NotifierRoot;
|
|
72
|
+
export default renderer;
|