@hexdspace/react 0.0.8 → 0.0.9
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/index.d.ts +77 -7
- package/dist/index.js +256 -96
- package/package.json +5 -2
- package/dist/index.css +0 -79
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
2
2
|
import { QueryClient, QueryKey } from '@tanstack/react-query';
|
|
3
3
|
import { ResultOk, ResultError } from '@hexdspace/util';
|
|
4
|
-
import React from 'react';
|
|
4
|
+
import React, { CSSProperties } from 'react';
|
|
5
5
|
|
|
6
6
|
type NotificationVariant = 'success' | 'warning' | 'error' | 'info';
|
|
7
7
|
declare const DEFAULT_NOTIFICATION_CHANNEL = "app.notifications";
|
|
@@ -39,17 +39,87 @@ declare class NotifierController {
|
|
|
39
39
|
}
|
|
40
40
|
declare const notifierController: NotifierController;
|
|
41
41
|
|
|
42
|
+
type ToastTransition = {
|
|
43
|
+
enter: string;
|
|
44
|
+
exit: string;
|
|
45
|
+
collapseDuration?: number;
|
|
46
|
+
};
|
|
47
|
+
type ToastTheme = {
|
|
48
|
+
lightBg?: string;
|
|
49
|
+
lightText?: string;
|
|
50
|
+
lightProgress?: string;
|
|
51
|
+
darkBg?: string;
|
|
52
|
+
darkText?: string;
|
|
53
|
+
darkProgress?: string;
|
|
54
|
+
progressInfo?: string;
|
|
55
|
+
progressSuccess?: string;
|
|
56
|
+
progressWarning?: string;
|
|
57
|
+
progressError?: string;
|
|
58
|
+
width?: string;
|
|
59
|
+
contentPadding?: string;
|
|
60
|
+
bodyColumnGap?: string;
|
|
61
|
+
bodyRowGap?: string;
|
|
62
|
+
transition?: ToastTransition;
|
|
63
|
+
action?: Partial<ToastActionTheme>;
|
|
64
|
+
iconColors?: Partial<Record<NotificationVariant, string>>;
|
|
65
|
+
};
|
|
66
|
+
type ToastifyCSSVars = CSSProperties & {
|
|
67
|
+
'--toastify-color-light'?: string;
|
|
68
|
+
'--toastify-text-color-light'?: string;
|
|
69
|
+
'--toastify-color-progress-light'?: string;
|
|
70
|
+
'--toastify-color-dark'?: string;
|
|
71
|
+
'--toastify-text-color-dark'?: string;
|
|
72
|
+
'--toastify-color-progress-dark'?: string;
|
|
73
|
+
'--toastify-color-progress-info'?: string;
|
|
74
|
+
'--toastify-color-progress-success'?: string;
|
|
75
|
+
'--toastify-color-progress-warning'?: string;
|
|
76
|
+
'--toastify-color-progress-error'?: string;
|
|
77
|
+
'--toastify-toast-width'?: string;
|
|
78
|
+
};
|
|
79
|
+
type ToastActionTheme = {
|
|
80
|
+
padding: string;
|
|
81
|
+
borderRadius: string;
|
|
82
|
+
background: string;
|
|
83
|
+
hoverBackground: string;
|
|
84
|
+
focusOutline: string;
|
|
85
|
+
focusOutlineOffset: string;
|
|
86
|
+
fontWeight: number | string;
|
|
87
|
+
transition: string;
|
|
88
|
+
focusTransform: string;
|
|
89
|
+
activeTransform: string;
|
|
90
|
+
color?: string;
|
|
91
|
+
hoverColor?: string;
|
|
92
|
+
marginLeft?: string;
|
|
93
|
+
marginTop?: string;
|
|
94
|
+
};
|
|
95
|
+
type ResolvedToastTheme = {
|
|
96
|
+
lightBg: string;
|
|
97
|
+
lightText: string;
|
|
98
|
+
lightProgress: string;
|
|
99
|
+
darkBg: string;
|
|
100
|
+
darkText: string;
|
|
101
|
+
darkProgress: string;
|
|
102
|
+
progressInfo: string;
|
|
103
|
+
progressSuccess: string;
|
|
104
|
+
progressWarning: string;
|
|
105
|
+
progressError: string;
|
|
106
|
+
width: string;
|
|
107
|
+
contentPadding: string;
|
|
108
|
+
bodyColumnGap: string;
|
|
109
|
+
bodyRowGap: string;
|
|
110
|
+
transition?: ToastTransition;
|
|
111
|
+
action: ToastActionTheme;
|
|
112
|
+
iconColors: Record<NotificationVariant, string>;
|
|
113
|
+
};
|
|
114
|
+
declare function resolveToastTheme(theme?: ToastTheme): ResolvedToastTheme;
|
|
115
|
+
|
|
42
116
|
type NotificationHostProps = {
|
|
43
117
|
channel?: string;
|
|
44
118
|
isDark?: () => boolean;
|
|
119
|
+
theme?: ToastTheme;
|
|
45
120
|
};
|
|
46
121
|
declare const NotificationHost: React.FC<NotificationHostProps>;
|
|
47
122
|
|
|
48
|
-
declare const NotifierProvider: React.FC<{
|
|
49
|
-
children: React.ReactNode;
|
|
50
|
-
}>;
|
|
51
|
-
declare function useNotifierController(): NotifierController;
|
|
52
|
-
|
|
53
123
|
type InstructionContext = {
|
|
54
124
|
queryClient: QueryClient;
|
|
55
125
|
};
|
|
@@ -93,4 +163,4 @@ type ResponsiveMutation<Args, Res> = {
|
|
|
93
163
|
|
|
94
164
|
declare function useResponsiveMutation<Args, Res>(responsiveMutation: ResponsiveMutation<Args, Res>, queryClient: QueryClient): _tanstack_react_query.UseMutationResult<UIOk<Res>, UIFail, Args, OptimisticSnapshot | undefined>;
|
|
95
165
|
|
|
96
|
-
export { type CacheInstruction, type CustomInstruction, DEFAULT_NOTIFICATION_CHANNEL, type Instruction, type InstructionContext, type Notification, type NotificationAction, NotificationHost, type NotificationInstruction, type NotificationVariant, NotifierController,
|
|
166
|
+
export { type CacheInstruction, type CustomInstruction, DEFAULT_NOTIFICATION_CHANNEL, type Instruction, type InstructionContext, type Notification, type NotificationAction, NotificationHost, type NotificationInstruction, type NotificationVariant, NotifierController, type OptimisticSnapshot, type ResolvedToastTheme, type ResponsiveMutation, type ToastActionTheme, type ToastTheme, type ToastTransition, type ToastifyCSSVars, type UIFail, type UIOk, type UIResult, notifierController, resolveToastTheme, ui, useResponsiveMutation };
|
package/dist/index.js
CHANGED
|
@@ -102,50 +102,272 @@ var subscribe = new SubscribeUseCase(bus);
|
|
|
102
102
|
var notifierController = new NotifierController(sendNotification, subscribe);
|
|
103
103
|
|
|
104
104
|
// src/feature/notifier/infra/web/react/NotificationHost.tsx
|
|
105
|
-
import { useEffect, useMemo as useMemo2 } from "react";
|
|
106
|
-
import {
|
|
107
|
-
import { AlertTriangleIcon, CheckCircleIcon, InfoIcon, XCircleIcon } from "lucide-react";
|
|
105
|
+
import { useCallback, useEffect, useMemo as useMemo2 } from "react";
|
|
106
|
+
import { toast as toast2, ToastContainer } from "react-toastify";
|
|
108
107
|
|
|
109
108
|
// src/feature/notifier/entity/notification.ts
|
|
110
109
|
var DEFAULT_NOTIFICATION_CHANNEL = "app.notifications";
|
|
111
110
|
|
|
112
|
-
// src/feature/notifier/infra/web/react/NotifierProvider.tsx
|
|
113
|
-
import { createContext, useContext, useMemo } from "react";
|
|
114
|
-
import { jsx } from "react/jsx-runtime";
|
|
115
|
-
var NotifierContext = createContext(null);
|
|
116
|
-
var NotifierProvider = ({ children }) => {
|
|
117
|
-
const controller = useMemo(() => notifierController, []);
|
|
118
|
-
return /* @__PURE__ */ jsx(NotifierContext.Provider, { value: { controller }, children });
|
|
119
|
-
};
|
|
120
|
-
function useNotifierController() {
|
|
121
|
-
const ctx = useContext(NotifierContext);
|
|
122
|
-
if (!ctx) throw new Error("useNotifierController must be used within <NotifierProvider>");
|
|
123
|
-
return ctx.controller;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
111
|
// src/feature/notifier/infra/web/react/CustomToastTransition.tsx
|
|
127
112
|
import { cssTransition } from "react-toastify";
|
|
128
113
|
var SlideUp = cssTransition({
|
|
129
114
|
enter: "slideIn",
|
|
130
115
|
exit: "slideOut",
|
|
131
|
-
collapseDuration: 300
|
|
132
|
-
appendPosition: false,
|
|
133
|
-
collapse: true
|
|
116
|
+
collapseDuration: 300
|
|
134
117
|
});
|
|
118
|
+
var buildToastTransition = (transition) => {
|
|
119
|
+
if (!transition) return SlideUp;
|
|
120
|
+
return cssTransition(transition);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// src/feature/notifier/infra/web/react/ToastContent.tsx
|
|
124
|
+
import { useMemo, useState } from "react";
|
|
125
|
+
import { toast } from "react-toastify";
|
|
126
|
+
import { AlertTriangleIcon, CheckCircleIcon, InfoIcon, XCircleIcon } from "lucide-react";
|
|
127
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
128
|
+
var DEFAULT_NOTIFICATION_VARIANT = "info";
|
|
129
|
+
var TOAST_ICON_BY_VARIANT = {
|
|
130
|
+
success: CheckCircleIcon,
|
|
131
|
+
warning: AlertTriangleIcon,
|
|
132
|
+
error: XCircleIcon,
|
|
133
|
+
info: InfoIcon
|
|
134
|
+
};
|
|
135
|
+
var ToastContent = ({ notification, theme, mode }) => {
|
|
136
|
+
const { title, content, action } = notification;
|
|
137
|
+
const variant = notification.variant ?? DEFAULT_NOTIFICATION_VARIANT;
|
|
138
|
+
const Icon = TOAST_ICON_BY_VARIANT[variant];
|
|
139
|
+
const iconColor = theme.iconColors[variant];
|
|
140
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
141
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
142
|
+
const [isActive, setIsActive] = useState(false);
|
|
143
|
+
const textColor = mode === "dark" ? theme.darkText : theme.lightText;
|
|
144
|
+
const actionTextColor = theme.action.color ?? textColor;
|
|
145
|
+
const actionHoverColor = theme.action.hoverColor ?? actionTextColor;
|
|
146
|
+
const actionStyle = useMemo(() => {
|
|
147
|
+
const background = isHovered ? theme.action.hoverBackground : theme.action.background;
|
|
148
|
+
const color = isHovered ? actionHoverColor : actionTextColor;
|
|
149
|
+
const transform = isActive ? theme.action.activeTransform : isFocused ? theme.action.focusTransform : void 0;
|
|
150
|
+
return {
|
|
151
|
+
alignSelf: "flex-start",
|
|
152
|
+
padding: theme.action.padding,
|
|
153
|
+
borderRadius: theme.action.borderRadius,
|
|
154
|
+
background,
|
|
155
|
+
color,
|
|
156
|
+
font: "inherit",
|
|
157
|
+
fontWeight: theme.action.fontWeight,
|
|
158
|
+
cursor: "pointer",
|
|
159
|
+
transition: theme.action.transition,
|
|
160
|
+
outline: isFocused ? theme.action.focusOutline : "none",
|
|
161
|
+
outlineOffset: isFocused ? theme.action.focusOutlineOffset : void 0,
|
|
162
|
+
transform,
|
|
163
|
+
marginLeft: theme.action.marginLeft
|
|
164
|
+
};
|
|
165
|
+
}, [actionHoverColor, actionTextColor, isActive, isFocused, isHovered, theme.action]);
|
|
166
|
+
const handleActionClick = async () => {
|
|
167
|
+
if (!action) return;
|
|
168
|
+
try {
|
|
169
|
+
await action.onClick?.();
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error("Notification action failed", error);
|
|
172
|
+
} finally {
|
|
173
|
+
toast.dismiss(notification.id);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
return /* @__PURE__ */ jsxs(
|
|
177
|
+
"div",
|
|
178
|
+
{
|
|
179
|
+
style: {
|
|
180
|
+
padding: theme.contentPadding,
|
|
181
|
+
display: "flex",
|
|
182
|
+
flexDirection: "column",
|
|
183
|
+
gap: "0.5rem",
|
|
184
|
+
width: "100%",
|
|
185
|
+
textAlign: "left"
|
|
186
|
+
},
|
|
187
|
+
children: [
|
|
188
|
+
/* @__PURE__ */ jsxs(
|
|
189
|
+
"div",
|
|
190
|
+
{
|
|
191
|
+
style: {
|
|
192
|
+
display: "flex",
|
|
193
|
+
alignItems: "center",
|
|
194
|
+
gap: "0.5rem"
|
|
195
|
+
},
|
|
196
|
+
children: [
|
|
197
|
+
/* @__PURE__ */ jsx(
|
|
198
|
+
Icon,
|
|
199
|
+
{
|
|
200
|
+
style: { color: iconColor, padding: "0.125rem", width: "1.25rem", height: "1.25rem", flexShrink: 0 },
|
|
201
|
+
"aria-hidden": "true"
|
|
202
|
+
}
|
|
203
|
+
),
|
|
204
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold leading-tight", children: title })
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
),
|
|
208
|
+
(content || action) && /* @__PURE__ */ jsxs(
|
|
209
|
+
"div",
|
|
210
|
+
{
|
|
211
|
+
style: {
|
|
212
|
+
display: "grid",
|
|
213
|
+
gridTemplateColumns: action ? "1fr auto" : "1fr",
|
|
214
|
+
columnGap: theme.bodyColumnGap,
|
|
215
|
+
rowGap: theme.bodyRowGap,
|
|
216
|
+
alignItems: "center",
|
|
217
|
+
width: "100%"
|
|
218
|
+
},
|
|
219
|
+
children: [
|
|
220
|
+
content && /* @__PURE__ */ jsx(
|
|
221
|
+
"span",
|
|
222
|
+
{
|
|
223
|
+
style: {
|
|
224
|
+
minWidth: 0,
|
|
225
|
+
textAlign: "left",
|
|
226
|
+
fontSize: "0.875rem",
|
|
227
|
+
color: textColor,
|
|
228
|
+
opacity: 0.9
|
|
229
|
+
},
|
|
230
|
+
children: content
|
|
231
|
+
}
|
|
232
|
+
),
|
|
233
|
+
action && /* @__PURE__ */ jsx(
|
|
234
|
+
"button",
|
|
235
|
+
{
|
|
236
|
+
type: "button",
|
|
237
|
+
style: {
|
|
238
|
+
...actionStyle,
|
|
239
|
+
marginTop: theme.action.marginTop,
|
|
240
|
+
fontSize: "0.875rem"
|
|
241
|
+
},
|
|
242
|
+
onMouseEnter: () => setIsHovered(true),
|
|
243
|
+
onMouseLeave: () => {
|
|
244
|
+
setIsHovered(false);
|
|
245
|
+
setIsActive(false);
|
|
246
|
+
},
|
|
247
|
+
onFocus: () => setIsFocused(true),
|
|
248
|
+
onBlur: () => {
|
|
249
|
+
setIsFocused(false);
|
|
250
|
+
setIsActive(false);
|
|
251
|
+
},
|
|
252
|
+
onMouseDown: () => setIsActive(true),
|
|
253
|
+
onMouseUp: () => setIsActive(false),
|
|
254
|
+
onClick: handleActionClick,
|
|
255
|
+
children: action.label
|
|
256
|
+
}
|
|
257
|
+
)
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// src/feature/notifier/entity/toast-theme.ts
|
|
267
|
+
var DEFAULT_ACTION_THEME = {
|
|
268
|
+
padding: "0.35rem 0.75rem",
|
|
269
|
+
borderRadius: "0.5rem",
|
|
270
|
+
background: "rgba(79, 70, 229, 0.12)",
|
|
271
|
+
hoverBackground: "rgba(79, 70, 229, 0.2)",
|
|
272
|
+
focusOutline: "2px solid #4338ca",
|
|
273
|
+
focusOutlineOffset: "2px",
|
|
274
|
+
fontWeight: 600,
|
|
275
|
+
transition: "background-color 0.2s ease, color 0.2s ease, transform 0.2s ease",
|
|
276
|
+
focusTransform: "translateY(-1px)",
|
|
277
|
+
activeTransform: "translateY(0)",
|
|
278
|
+
marginLeft: "auto",
|
|
279
|
+
marginTop: "0.15rem"
|
|
280
|
+
};
|
|
281
|
+
var DEFAULT_ICON_COLORS = {
|
|
282
|
+
info: "#2563eb",
|
|
283
|
+
success: "#16a34a",
|
|
284
|
+
warning: "#f59e0b",
|
|
285
|
+
error: "#dc2626"
|
|
286
|
+
};
|
|
287
|
+
var DEFAULT_THEME_BASE = {
|
|
288
|
+
lightBg: "#f8fafc",
|
|
289
|
+
lightText: "#0f172a",
|
|
290
|
+
lightProgress: "#4f46e5",
|
|
291
|
+
darkBg: "#111827",
|
|
292
|
+
darkText: "#e5e7eb",
|
|
293
|
+
darkProgress: "#a5b4fc",
|
|
294
|
+
progressInfo: "#60a5fa",
|
|
295
|
+
progressSuccess: "#34d399",
|
|
296
|
+
progressWarning: "#fbbf24",
|
|
297
|
+
progressError: "#f87171",
|
|
298
|
+
width: "min(24rem, calc(100vw - 2rem))",
|
|
299
|
+
contentPadding: "0.5rem 0.75rem 1rem 0.5rem",
|
|
300
|
+
bodyColumnGap: "0.75rem",
|
|
301
|
+
bodyRowGap: "0.25rem",
|
|
302
|
+
transition: {
|
|
303
|
+
enter: "slideIn",
|
|
304
|
+
exit: "slideOut",
|
|
305
|
+
collapseDuration: 300
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
function resolveToastTheme(theme) {
|
|
309
|
+
return {
|
|
310
|
+
lightBg: theme?.lightBg ?? DEFAULT_THEME_BASE.lightBg,
|
|
311
|
+
lightText: theme?.lightText ?? DEFAULT_THEME_BASE.lightText,
|
|
312
|
+
lightProgress: theme?.lightProgress ?? DEFAULT_THEME_BASE.lightProgress,
|
|
313
|
+
darkBg: theme?.darkBg ?? DEFAULT_THEME_BASE.darkBg,
|
|
314
|
+
darkText: theme?.darkText ?? DEFAULT_THEME_BASE.darkText,
|
|
315
|
+
darkProgress: theme?.darkProgress ?? DEFAULT_THEME_BASE.darkProgress,
|
|
316
|
+
progressInfo: theme?.progressInfo ?? DEFAULT_THEME_BASE.progressInfo,
|
|
317
|
+
progressSuccess: theme?.progressSuccess ?? DEFAULT_THEME_BASE.progressSuccess,
|
|
318
|
+
progressWarning: theme?.progressWarning ?? DEFAULT_THEME_BASE.progressWarning,
|
|
319
|
+
progressError: theme?.progressError ?? DEFAULT_THEME_BASE.progressError,
|
|
320
|
+
width: theme?.width ?? DEFAULT_THEME_BASE.width,
|
|
321
|
+
contentPadding: theme?.contentPadding ?? DEFAULT_THEME_BASE.contentPadding,
|
|
322
|
+
bodyColumnGap: theme?.bodyColumnGap ?? DEFAULT_THEME_BASE.bodyColumnGap,
|
|
323
|
+
bodyRowGap: theme?.bodyRowGap ?? DEFAULT_THEME_BASE.bodyRowGap,
|
|
324
|
+
transition: theme?.transition ?? DEFAULT_THEME_BASE.transition,
|
|
325
|
+
action: {
|
|
326
|
+
...DEFAULT_ACTION_THEME,
|
|
327
|
+
...theme?.action ?? {}
|
|
328
|
+
},
|
|
329
|
+
iconColors: {
|
|
330
|
+
...DEFAULT_ICON_COLORS,
|
|
331
|
+
...theme?.iconColors ?? {}
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
}
|
|
135
335
|
|
|
136
336
|
// src/feature/notifier/infra/web/react/NotificationHost.tsx
|
|
137
|
-
import { jsx as jsx2
|
|
138
|
-
var NotificationHost = ({ channel = DEFAULT_NOTIFICATION_CHANNEL, isDark }) => {
|
|
139
|
-
const
|
|
337
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
338
|
+
var NotificationHost = ({ channel = DEFAULT_NOTIFICATION_CHANNEL, isDark, theme }) => {
|
|
339
|
+
const resolvedTheme = useMemo2(() => resolveToastTheme(theme), [theme]);
|
|
340
|
+
const mode = useMemo2(() => {
|
|
341
|
+
if (!isDark) {
|
|
342
|
+
return "light";
|
|
343
|
+
}
|
|
344
|
+
return isDark() ? "dark" : "light";
|
|
345
|
+
}, [isDark]);
|
|
346
|
+
const style = useMemo2(() => ({
|
|
347
|
+
"--toastify-color-light": resolvedTheme.lightBg,
|
|
348
|
+
"--toastify-text-color-light": resolvedTheme.lightText,
|
|
349
|
+
"--toastify-color-progress-light": resolvedTheme.lightProgress,
|
|
350
|
+
"--toastify-color-dark": resolvedTheme.darkBg,
|
|
351
|
+
"--toastify-text-color-dark": resolvedTheme.darkText,
|
|
352
|
+
"--toastify-color-progress-dark": resolvedTheme.darkProgress,
|
|
353
|
+
"--toastify-color-progress-info": resolvedTheme.progressInfo,
|
|
354
|
+
"--toastify-color-progress-success": resolvedTheme.progressSuccess,
|
|
355
|
+
"--toastify-color-progress-warning": resolvedTheme.progressWarning,
|
|
356
|
+
"--toastify-color-progress-error": resolvedTheme.progressError,
|
|
357
|
+
"--toastify-toast-width": resolvedTheme.width
|
|
358
|
+
}), [resolvedTheme]);
|
|
359
|
+
const renderToast = useCallback((notification) => {
|
|
360
|
+
toast2(/* @__PURE__ */ jsx2(ToastContent, { notification, theme: resolvedTheme, mode }), {
|
|
361
|
+
toastId: notification.id,
|
|
362
|
+
icon: false
|
|
363
|
+
});
|
|
364
|
+
}, [mode, resolvedTheme]);
|
|
365
|
+
const transition = useMemo2(() => buildToastTransition(resolvedTheme.transition), [resolvedTheme.transition]);
|
|
140
366
|
useEffect(() => {
|
|
141
367
|
let unsub;
|
|
142
368
|
let disposed = false;
|
|
143
|
-
const listener = {
|
|
144
|
-
|
|
145
|
-
renderToast(notification);
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
controller.handleSubscribe(channel, listener).then((unsubHandler) => {
|
|
369
|
+
const listener = { notify: renderToast };
|
|
370
|
+
notifierController.handleSubscribe(channel, listener).then((unsubHandler) => {
|
|
149
371
|
if (disposed) {
|
|
150
372
|
unsubHandler();
|
|
151
373
|
return;
|
|
@@ -158,84 +380,23 @@ var NotificationHost = ({ channel = DEFAULT_NOTIFICATION_CHANNEL, isDark }) => {
|
|
|
158
380
|
disposed = true;
|
|
159
381
|
if (unsub) unsub();
|
|
160
382
|
};
|
|
161
|
-
}, [channel,
|
|
162
|
-
const theme = useMemo2(() => {
|
|
163
|
-
if (!isDark) {
|
|
164
|
-
return "light";
|
|
165
|
-
}
|
|
166
|
-
return isDark() ? "dark" : "light";
|
|
167
|
-
}, []);
|
|
383
|
+
}, [channel, renderToast]);
|
|
168
384
|
return /* @__PURE__ */ jsx2(
|
|
169
385
|
ToastContainer,
|
|
170
386
|
{
|
|
387
|
+
style,
|
|
171
388
|
position: "bottom-right",
|
|
172
|
-
transition
|
|
389
|
+
transition,
|
|
173
390
|
newestOnTop: true,
|
|
174
391
|
draggable: true,
|
|
175
392
|
pauseOnFocusLoss: false,
|
|
176
393
|
icon: false,
|
|
177
394
|
pauseOnHover: true,
|
|
178
395
|
hideProgressBar: false,
|
|
179
|
-
|
|
180
|
-
theme
|
|
396
|
+
theme: mode
|
|
181
397
|
}
|
|
182
398
|
);
|
|
183
399
|
};
|
|
184
|
-
var DEFAULT_NOTIFICATION_VARIANT = "info";
|
|
185
|
-
var TOAST_ICON_BY_VARIANT = {
|
|
186
|
-
success: { Icon: CheckCircleIcon, color: "var(--success)" },
|
|
187
|
-
warning: { Icon: AlertTriangleIcon, color: "var(--warning)" },
|
|
188
|
-
error: { Icon: XCircleIcon, color: "var(--danger)" },
|
|
189
|
-
info: { Icon: InfoIcon, color: "var(--accent)" }
|
|
190
|
-
};
|
|
191
|
-
function renderToast(notification) {
|
|
192
|
-
const variant = notification.variant ?? DEFAULT_NOTIFICATION_VARIANT;
|
|
193
|
-
toast(/* @__PURE__ */ jsx2(ToastContent, { notification }), {
|
|
194
|
-
type: variant,
|
|
195
|
-
toastId: notification.id,
|
|
196
|
-
icon: false
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
var ToastContent = ({ notification }) => {
|
|
200
|
-
const { title, content, action } = notification;
|
|
201
|
-
const variant = notification.variant ?? DEFAULT_NOTIFICATION_VARIANT;
|
|
202
|
-
const { Icon, color } = TOAST_ICON_BY_VARIANT[variant];
|
|
203
|
-
const handleActionClick = async () => {
|
|
204
|
-
if (!action) return;
|
|
205
|
-
try {
|
|
206
|
-
await action.onClick?.();
|
|
207
|
-
} catch (error) {
|
|
208
|
-
console.error("Notification action failed", error);
|
|
209
|
-
} finally {
|
|
210
|
-
toast.dismiss(notification.id);
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
return /* @__PURE__ */ jsxs("div", { className: "toast-content flex w-full flex-col gap-2", children: [
|
|
214
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
215
|
-
/* @__PURE__ */ jsx2(
|
|
216
|
-
Icon,
|
|
217
|
-
{
|
|
218
|
-
className: "h-4 w-4 shrink-0",
|
|
219
|
-
style: { color },
|
|
220
|
-
"aria-hidden": "true"
|
|
221
|
-
}
|
|
222
|
-
),
|
|
223
|
-
/* @__PURE__ */ jsx2("span", { className: "font-semibold", children: title })
|
|
224
|
-
] }),
|
|
225
|
-
(content || action) && /* @__PURE__ */ jsxs("div", { className: "flex w-full items-start gap-3", children: [
|
|
226
|
-
content && /* @__PURE__ */ jsx2("span", { className: "flex-1 text-sm text-muted-foreground", children: content }),
|
|
227
|
-
action && /* @__PURE__ */ jsx2(
|
|
228
|
-
"button",
|
|
229
|
-
{
|
|
230
|
-
type: "button",
|
|
231
|
-
className: "toast-action text-sm font-medium ml-auto shrink-0 self-end",
|
|
232
|
-
onClick: handleActionClick,
|
|
233
|
-
children: action.label
|
|
234
|
-
}
|
|
235
|
-
)
|
|
236
|
-
] })
|
|
237
|
-
] });
|
|
238
|
-
};
|
|
239
400
|
|
|
240
401
|
// src/util/responsive-query/use-responsive-mutation.ts
|
|
241
402
|
function useResponsiveMutation(responsiveMutation, queryClient) {
|
|
@@ -281,9 +442,8 @@ export {
|
|
|
281
442
|
DEFAULT_NOTIFICATION_CHANNEL,
|
|
282
443
|
NotificationHost,
|
|
283
444
|
NotifierController,
|
|
284
|
-
NotifierProvider,
|
|
285
445
|
notifierController,
|
|
446
|
+
resolveToastTheme,
|
|
286
447
|
ui,
|
|
287
|
-
useNotifierController,
|
|
288
448
|
useResponsiveMutation
|
|
289
449
|
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hexdspace/react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
|
-
".":
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
9
12
|
},
|
|
10
13
|
"files": [
|
|
11
14
|
"dist"
|
package/dist/index.css
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/* src/feature/notifier/infra/web/react/notification-host.css */
|
|
2
|
-
@layer base {
|
|
3
|
-
.custom-toast-container {
|
|
4
|
-
--toastify-color-light: var(--surface-3);
|
|
5
|
-
--toastify-text-color-light: var(--text-1);
|
|
6
|
-
--toastify-color-progress-light: color-mix(in oklab, var(--accent-high), transparent 30%);
|
|
7
|
-
--toastify-color-dark: var(--surface-1);
|
|
8
|
-
--toastify-text-color-dark: var(--text-1);
|
|
9
|
-
--toastify-color-progress-dark: color-mix(in oklab, var(--accent-high), transparent 30%);
|
|
10
|
-
--toastify-color-progress-info: color-mix(in oklab, var(--accent), transparent 30%);
|
|
11
|
-
--toastify-color-progress-success: color-mix(in oklab, var(--success), transparent 30%);
|
|
12
|
-
--toastify-color-progress-warning: color-mix(in oklab, var(--warning), transparent 30%);
|
|
13
|
-
--toastify-color-progress-error: color-mix(in oklab, var(--danger), transparent 30%);
|
|
14
|
-
--toastify-toast-width: min(24rem, calc(100vw - 2rem));
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
@supports (color-mix(in oklab, white, transparent)) {
|
|
18
|
-
@layer base {
|
|
19
|
-
.custom-toast-container {
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
@layer components {
|
|
24
|
-
.toast-content {
|
|
25
|
-
padding: 0.5rem 0.75rem 1rem 0.5rem;
|
|
26
|
-
}
|
|
27
|
-
.toast-action {
|
|
28
|
-
align-self: flex-start;
|
|
29
|
-
padding: 0.35rem 0.75rem;
|
|
30
|
-
border-radius: var(--shape-radius-btn);
|
|
31
|
-
background: color-mix(in oklab, var(--secondary-3), transparent 30%);
|
|
32
|
-
color: var(--color-contrast-inverted);
|
|
33
|
-
font: inherit;
|
|
34
|
-
font-weight: 600;
|
|
35
|
-
cursor: pointer;
|
|
36
|
-
transition:
|
|
37
|
-
background-color 0.2s ease,
|
|
38
|
-
color 0.2s ease,
|
|
39
|
-
transform 0.2s ease;
|
|
40
|
-
}
|
|
41
|
-
.toast-action:hover {
|
|
42
|
-
background: var(--secondary-3);
|
|
43
|
-
color: var(--contrast-inverted);
|
|
44
|
-
}
|
|
45
|
-
.toast-action:focus-visible {
|
|
46
|
-
outline: 2px solid var(--focus);
|
|
47
|
-
outline-offset: 2px;
|
|
48
|
-
transform: translateY(-1px);
|
|
49
|
-
}
|
|
50
|
-
.toast-action:active {
|
|
51
|
-
transform: translateY(0);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
@keyframes slideIn {
|
|
55
|
-
from {
|
|
56
|
-
transform: translateY(-20px);
|
|
57
|
-
opacity: 0;
|
|
58
|
-
}
|
|
59
|
-
to {
|
|
60
|
-
transform: translateY(0);
|
|
61
|
-
opacity: 1;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
@keyframes slideOut {
|
|
65
|
-
from {
|
|
66
|
-
transform: translateY(0);
|
|
67
|
-
opacity: 1;
|
|
68
|
-
}
|
|
69
|
-
to {
|
|
70
|
-
transform: translateY(-20px);
|
|
71
|
-
opacity: 0;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
.slideIn {
|
|
75
|
-
animation: slideIn 0.35s forwards;
|
|
76
|
-
}
|
|
77
|
-
.slideOut {
|
|
78
|
-
animation: slideOut 0.25s forwards;
|
|
79
|
-
}
|