@heymantle/litho 0.0.13 → 0.0.15
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 +52 -0
- package/dist/cjs/components/Card.js +1 -1
- package/dist/cjs/components/Disclosure.js +46 -15
- package/dist/cjs/components/DropZone.js +93 -37
- package/dist/cjs/components/Layout.js +4 -2
- package/dist/cjs/components/Modal.js +14 -3
- package/dist/cjs/components/Navigation.js +4 -3
- package/dist/cjs/components/Popover.js +58 -13
- package/dist/cjs/components/Select.js +4 -0
- package/dist/cjs/components/TabNavigation.js +65 -11
- package/dist/cjs/components/Table.js +27 -11
- package/dist/cjs/components/Tabs.js +33 -2
- package/dist/cjs/components/TextField.js +4 -2
- package/dist/cjs/components/ToastNotification.js +368 -0
- package/dist/cjs/components/ToastProvider.js +342 -0
- package/dist/cjs/index.js +11 -0
- package/dist/cjs/playwright.config.js +114 -0
- package/dist/cjs/styles/Table.js +2 -7
- package/dist/cjs/tests/visual/stories.spec.js +637 -0
- package/dist/cjs/utilities/dates.js +7 -7
- package/dist/esm/components/Card.js +1 -1
- package/dist/esm/components/Disclosure.js +36 -5
- package/dist/esm/components/DropZone.js +94 -38
- package/dist/esm/components/Layout.js +4 -2
- package/dist/esm/components/Modal.js +14 -3
- package/dist/esm/components/Navigation.js +4 -3
- package/dist/esm/components/Popover.js +58 -13
- package/dist/esm/components/Select.js +5 -1
- package/dist/esm/components/TabNavigation.js +65 -11
- package/dist/esm/components/Table.js +27 -11
- package/dist/esm/components/Tabs.js +33 -2
- package/dist/esm/components/TextField.js +4 -2
- package/dist/esm/components/ToastNotification.js +353 -0
- package/dist/esm/components/ToastProvider.js +336 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/playwright.config.js +104 -0
- package/dist/esm/styles/Table.js +2 -7
- package/dist/esm/tests/visual/stories.spec.js +633 -0
- package/dist/esm/utilities/dates.js +7 -7
- package/dist/types/components/Disclosure.d.ts.map +1 -1
- package/dist/types/components/DropZone.d.ts +2 -0
- package/dist/types/components/DropZone.d.ts.map +1 -1
- package/dist/types/components/Layout.d.ts.map +1 -1
- package/dist/types/components/Modal.d.ts.map +1 -1
- package/dist/types/components/Navigation.d.ts +1 -0
- package/dist/types/components/Navigation.d.ts.map +1 -1
- package/dist/types/components/Popover.d.ts +2 -0
- package/dist/types/components/Popover.d.ts.map +1 -1
- package/dist/types/components/Select.d.ts.map +1 -1
- package/dist/types/components/TabNavigation.d.ts.map +1 -1
- package/dist/types/components/Table.d.ts.map +1 -1
- package/dist/types/components/Tabs.d.ts +45 -1
- package/dist/types/components/Tabs.d.ts.map +1 -1
- package/dist/types/components/TextField.d.ts +2 -0
- package/dist/types/components/TextField.d.ts.map +1 -1
- package/dist/types/components/ToastNotification.d.ts +36 -0
- package/dist/types/components/ToastNotification.d.ts.map +1 -0
- package/dist/types/components/ToastProvider.d.ts +21 -0
- package/dist/types/components/ToastProvider.d.ts.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/styles/Table.d.ts.map +1 -1
- package/index.css +3 -0
- package/package.json +12 -3
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
function _array_like_to_array(arr, len) {
|
|
3
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
4
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
5
|
+
return arr2;
|
|
6
|
+
}
|
|
7
|
+
function _array_with_holes(arr) {
|
|
8
|
+
if (Array.isArray(arr)) return arr;
|
|
9
|
+
}
|
|
10
|
+
function _iterable_to_array_limit(arr, i) {
|
|
11
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
12
|
+
if (_i == null) return;
|
|
13
|
+
var _arr = [];
|
|
14
|
+
var _n = true;
|
|
15
|
+
var _d = false;
|
|
16
|
+
var _s, _e;
|
|
17
|
+
try {
|
|
18
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
19
|
+
_arr.push(_s.value);
|
|
20
|
+
if (i && _arr.length === i) break;
|
|
21
|
+
}
|
|
22
|
+
} catch (err) {
|
|
23
|
+
_d = true;
|
|
24
|
+
_e = err;
|
|
25
|
+
} finally{
|
|
26
|
+
try {
|
|
27
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
28
|
+
} finally{
|
|
29
|
+
if (_d) throw _e;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return _arr;
|
|
33
|
+
}
|
|
34
|
+
function _non_iterable_rest() {
|
|
35
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
36
|
+
}
|
|
37
|
+
function _sliced_to_array(arr, i) {
|
|
38
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
39
|
+
}
|
|
40
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
41
|
+
if (!o) return;
|
|
42
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
43
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
44
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
45
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
46
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
47
|
+
}
|
|
48
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
49
|
+
import { useEffect, useState, useRef, useCallback } from "react";
|
|
50
|
+
import { tv } from "tailwind-variants";
|
|
51
|
+
import Text from "./Text.js";
|
|
52
|
+
import Icon from "./Icon.js";
|
|
53
|
+
import { CancelMinor } from "@shopify/polaris-icons";
|
|
54
|
+
var styles = tv({
|
|
55
|
+
base: "Litho-ToastNotification group/toast pointer-events-auto min-w-60 max-w-85 bg-surface-highest/95 hover:bg-surface-highest rounded-md shadow-card border border-tint-2 p-2 flex items-start gap-2 transition-all duration-300 ease-in-out",
|
|
56
|
+
variants: {
|
|
57
|
+
position: {
|
|
58
|
+
topRight: "",
|
|
59
|
+
topLeft: "",
|
|
60
|
+
topCenter: "",
|
|
61
|
+
bottomRight: "",
|
|
62
|
+
bottomLeft: "",
|
|
63
|
+
bottomCenter: ""
|
|
64
|
+
},
|
|
65
|
+
fixed: {
|
|
66
|
+
true: "fixed z-[9999]",
|
|
67
|
+
false: "relative"
|
|
68
|
+
},
|
|
69
|
+
visible: {
|
|
70
|
+
true: "opacity-100 translate-y-0",
|
|
71
|
+
false: "opacity-0"
|
|
72
|
+
},
|
|
73
|
+
clickable: {
|
|
74
|
+
true: "cursor-pointer",
|
|
75
|
+
false: ""
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
defaultVariants: {
|
|
79
|
+
position: "topRight",
|
|
80
|
+
visible: true,
|
|
81
|
+
fixed: true,
|
|
82
|
+
clickable: false
|
|
83
|
+
},
|
|
84
|
+
compoundVariants: [
|
|
85
|
+
{
|
|
86
|
+
position: "topRight",
|
|
87
|
+
fixed: true,
|
|
88
|
+
class: "top-4 right-4"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
position: "topLeft",
|
|
92
|
+
fixed: true,
|
|
93
|
+
class: "top-4 left-4"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
position: "topCenter",
|
|
97
|
+
fixed: true,
|
|
98
|
+
class: "top-4 left-1/2 -translate-x-1/2"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
position: "bottomRight",
|
|
102
|
+
fixed: true,
|
|
103
|
+
class: "bottom-4 right-4"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
position: "bottomLeft",
|
|
107
|
+
fixed: true,
|
|
108
|
+
class: "bottom-4 left-4"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
position: "bottomCenter",
|
|
112
|
+
fixed: true,
|
|
113
|
+
class: "bottom-4 left-1/2 -translate-x-1/2"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
position: "topRight",
|
|
117
|
+
visible: false,
|
|
118
|
+
class: "translate-x-full"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
position: "topLeft",
|
|
122
|
+
visible: false,
|
|
123
|
+
class: "-translate-x-full"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
position: "topCenter",
|
|
127
|
+
visible: false,
|
|
128
|
+
class: "-translate-y-4"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
position: "bottomRight",
|
|
132
|
+
visible: false,
|
|
133
|
+
class: "translate-x-full"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
position: "bottomLeft",
|
|
137
|
+
visible: false,
|
|
138
|
+
class: "-translate-x-full"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
position: "bottomCenter",
|
|
142
|
+
visible: false,
|
|
143
|
+
class: "translate-y-4"
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
});
|
|
147
|
+
var closeButtonStyles = tv({
|
|
148
|
+
base: "Litho-ToastNotification__Close flex-none p-0.5 rounded-md cursor-pointer hover:bg-tint-2 active:bg-tint-3 transition-colors opacity-0 group-hover/toast:opacity-100"
|
|
149
|
+
});
|
|
150
|
+
/**
|
|
151
|
+
* ToastNotification component for displaying temporary notifications to users.
|
|
152
|
+
* Automatically dismisses after the specified duration.
|
|
153
|
+
*
|
|
154
|
+
* @component
|
|
155
|
+
* @param {Object} props - The component props.
|
|
156
|
+
* @param {React.ReactNode} [props.prefix] - Optional prefix content (image, icon, or component) to display before the title.
|
|
157
|
+
* @param {Object|Function} [props.icon] - Optional icon source (from @shopify/polaris-icons) to display as prefix. Used as fallback if prefix is not provided.
|
|
158
|
+
* @param {string} props.title - The main title text of the toast.
|
|
159
|
+
* @param {string} [props.subtitle] - Optional subtitle text displayed below the title.
|
|
160
|
+
* @param {number} [props.duration=3000] - Duration in milliseconds before the toast auto-dismisses. Defaults to 2000ms (2 seconds).
|
|
161
|
+
* @param {'topRight' | 'topLeft' | 'topCenter' | 'bottomRight' | 'bottomLeft' | 'bottomCenter'} [props.position='topRight'] - Position of the toast on the screen.
|
|
162
|
+
* @param {Function} [props.onDismiss] - Callback function called when the toast is dismissed (either automatically or manually).
|
|
163
|
+
* @param {Function} [props.onClick] - Callback function called when the toast is clicked. Makes the entire toast clickable with cursor-pointer.
|
|
164
|
+
* @param {boolean} [props.dismissible=true] - Whether the toast can be manually dismissed via the close button.
|
|
165
|
+
* @param {boolean} [props.fixed=true] - Whether to use fixed positioning. Set to false when used within ToastProvider.
|
|
166
|
+
* @param {boolean} [props.isDismissing=false] - Whether the toast is in a dismissing state. Starts the exit animation immediately.
|
|
167
|
+
* @param {string} [props.className] - Additional CSS classes to apply to the toast container.
|
|
168
|
+
* @returns {JSX.Element|null} The rendered toast notification or null if dismissed.
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* <ToastNotification
|
|
172
|
+
* prefix={<Icon source={TickMinor} color="success" />}
|
|
173
|
+
* title="Support ticket updated"
|
|
174
|
+
* subtitle="Your ticket has been resolved"
|
|
175
|
+
* duration={3000}
|
|
176
|
+
* position="topRight"
|
|
177
|
+
* onDismiss={() => console.log('Toast dismissed')}
|
|
178
|
+
* />
|
|
179
|
+
*/ function ToastNotification() {
|
|
180
|
+
var props = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
181
|
+
var prefix = props.prefix, icon = props.icon, title = props.title, subtitle = props.subtitle, _props_duration = props.duration, duration = _props_duration === void 0 ? 3000 : _props_duration, _props_position = props.position, position = _props_position === void 0 ? "topRight" : _props_position, onDismiss = props.onDismiss, onClick = props.onClick, _props_dismissible = props.dismissible, dismissible = _props_dismissible === void 0 ? true : _props_dismissible, _props_fixed = props.fixed, fixed = _props_fixed === void 0 ? true : _props_fixed, _props_isDismissing = props.isDismissing, isDismissing = _props_isDismissing === void 0 ? false : _props_isDismissing, className = props.className;
|
|
182
|
+
var _useState = _sliced_to_array(useState(!isDismissing), 2), visible = _useState[0], setVisible = _useState[1];
|
|
183
|
+
var _useState1 = _sliced_to_array(useState(true), 2), shouldRender = _useState1[0], setShouldRender = _useState1[1];
|
|
184
|
+
var _useState2 = _sliced_to_array(useState(false), 2), isHovered = _useState2[0], setIsHovered = _useState2[1];
|
|
185
|
+
var timerRef = useRef(null);
|
|
186
|
+
var startTimeRef = useRef(null);
|
|
187
|
+
var remainingTimeRef = useRef(duration);
|
|
188
|
+
var dismissStartedRef = useRef(false);
|
|
189
|
+
// If isDismissing is true, start exit animation immediately
|
|
190
|
+
useEffect(function() {
|
|
191
|
+
if (isDismissing) {
|
|
192
|
+
setVisible(false);
|
|
193
|
+
dismissStartedRef.current = true;
|
|
194
|
+
setTimeout(function() {
|
|
195
|
+
setShouldRender(false);
|
|
196
|
+
}, 300);
|
|
197
|
+
}
|
|
198
|
+
}, [
|
|
199
|
+
isDismissing
|
|
200
|
+
]);
|
|
201
|
+
var handleDismiss = useCallback(function() {
|
|
202
|
+
if (dismissStartedRef.current) {
|
|
203
|
+
return; // Already dismissing
|
|
204
|
+
}
|
|
205
|
+
dismissStartedRef.current = true;
|
|
206
|
+
setVisible(false);
|
|
207
|
+
// Wait for exit animation to complete before removing from DOM
|
|
208
|
+
setTimeout(function() {
|
|
209
|
+
setShouldRender(false);
|
|
210
|
+
if (onDismiss) {
|
|
211
|
+
onDismiss();
|
|
212
|
+
}
|
|
213
|
+
}, 300); // Match transition duration
|
|
214
|
+
}, [
|
|
215
|
+
onDismiss
|
|
216
|
+
]);
|
|
217
|
+
// Initialize remaining time when duration changes
|
|
218
|
+
useEffect(function() {
|
|
219
|
+
if (duration > 0) {
|
|
220
|
+
remainingTimeRef.current = duration;
|
|
221
|
+
}
|
|
222
|
+
}, [
|
|
223
|
+
duration
|
|
224
|
+
]);
|
|
225
|
+
// Set up auto-dismiss timer
|
|
226
|
+
useEffect(function() {
|
|
227
|
+
// Don't start timer if already dismissing, duration is 0, or hovered
|
|
228
|
+
if (isDismissing || duration <= 0 || isHovered || dismissStartedRef.current) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
// Clear any existing timer
|
|
232
|
+
if (timerRef.current) {
|
|
233
|
+
clearTimeout(timerRef.current);
|
|
234
|
+
timerRef.current = null;
|
|
235
|
+
}
|
|
236
|
+
// Start timer with remaining time
|
|
237
|
+
startTimeRef.current = Date.now();
|
|
238
|
+
timerRef.current = setTimeout(function() {
|
|
239
|
+
handleDismiss();
|
|
240
|
+
}, remainingTimeRef.current);
|
|
241
|
+
return function() {
|
|
242
|
+
if (timerRef.current) {
|
|
243
|
+
clearTimeout(timerRef.current);
|
|
244
|
+
timerRef.current = null;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}, [
|
|
248
|
+
duration,
|
|
249
|
+
isDismissing,
|
|
250
|
+
isHovered,
|
|
251
|
+
handleDismiss
|
|
252
|
+
]);
|
|
253
|
+
// Handle hover state changes
|
|
254
|
+
useEffect(function() {
|
|
255
|
+
if (isHovered && timerRef.current && startTimeRef.current && !dismissStartedRef.current) {
|
|
256
|
+
// Pause: calculate remaining time and clear timer
|
|
257
|
+
var elapsed = Date.now() - startTimeRef.current;
|
|
258
|
+
remainingTimeRef.current = Math.max(0, remainingTimeRef.current - elapsed);
|
|
259
|
+
clearTimeout(timerRef.current);
|
|
260
|
+
timerRef.current = null;
|
|
261
|
+
} else if (!isHovered && !isDismissing && duration > 0 && remainingTimeRef.current > 0 && visible && !dismissStartedRef.current) {
|
|
262
|
+
// Resume: restart timer with remaining time
|
|
263
|
+
if (timerRef.current) {
|
|
264
|
+
clearTimeout(timerRef.current);
|
|
265
|
+
}
|
|
266
|
+
startTimeRef.current = Date.now();
|
|
267
|
+
timerRef.current = setTimeout(function() {
|
|
268
|
+
handleDismiss();
|
|
269
|
+
}, remainingTimeRef.current);
|
|
270
|
+
}
|
|
271
|
+
}, [
|
|
272
|
+
isHovered,
|
|
273
|
+
isDismissing,
|
|
274
|
+
duration,
|
|
275
|
+
visible,
|
|
276
|
+
handleDismiss
|
|
277
|
+
]);
|
|
278
|
+
var handleMouseEnter = function() {
|
|
279
|
+
if (!isDismissing && visible && !dismissStartedRef.current) {
|
|
280
|
+
setIsHovered(true);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
var handleMouseLeave = function() {
|
|
284
|
+
if (!isDismissing && visible && !dismissStartedRef.current) {
|
|
285
|
+
setIsHovered(false);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
if (!shouldRender) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
var classes = styles({
|
|
292
|
+
position: position,
|
|
293
|
+
visible: visible,
|
|
294
|
+
fixed: fixed,
|
|
295
|
+
clickable: !!onClick
|
|
296
|
+
});
|
|
297
|
+
var closeClasses = closeButtonStyles();
|
|
298
|
+
var handleToastClick = function(e) {
|
|
299
|
+
if (onClick) {
|
|
300
|
+
onClick(e);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
var handleCloseClick = function(e) {
|
|
304
|
+
e.stopPropagation(); // Prevent triggering toast onClick
|
|
305
|
+
handleDismiss();
|
|
306
|
+
};
|
|
307
|
+
// Determine what to render as prefix: use prefix if provided, otherwise fallback to icon
|
|
308
|
+
var prefixContent = prefix || (icon ? /*#__PURE__*/ _jsx(Icon, {
|
|
309
|
+
source: icon,
|
|
310
|
+
color: "subdued"
|
|
311
|
+
}) : null);
|
|
312
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
313
|
+
className: "".concat(classes).concat(className ? " ".concat(className) : ""),
|
|
314
|
+
onClick: handleToastClick,
|
|
315
|
+
onMouseEnter: handleMouseEnter,
|
|
316
|
+
onMouseLeave: handleMouseLeave,
|
|
317
|
+
children: [
|
|
318
|
+
prefixContent && /*#__PURE__*/ _jsx("div", {
|
|
319
|
+
className: "Litho-ToastNotification__Prefix flex-none",
|
|
320
|
+
children: prefixContent
|
|
321
|
+
}),
|
|
322
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
323
|
+
className: "Litho-ToastNotification__Content flex-1 flex flex-col gap-0.5 min-w-0",
|
|
324
|
+
children: [
|
|
325
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
326
|
+
fontWeight: "medium",
|
|
327
|
+
variant: "bodySm",
|
|
328
|
+
clampLines: 1,
|
|
329
|
+
children: title
|
|
330
|
+
}),
|
|
331
|
+
subtitle && /*#__PURE__*/ _jsx(Text, {
|
|
332
|
+
variant: "bodySm",
|
|
333
|
+
color: "subdued",
|
|
334
|
+
clampLines: 2,
|
|
335
|
+
children: subtitle
|
|
336
|
+
})
|
|
337
|
+
]
|
|
338
|
+
}),
|
|
339
|
+
dismissible && /*#__PURE__*/ _jsx("button", {
|
|
340
|
+
type: "button",
|
|
341
|
+
onClick: handleCloseClick,
|
|
342
|
+
className: closeClasses,
|
|
343
|
+
"aria-label": "Dismiss notification",
|
|
344
|
+
children: /*#__PURE__*/ _jsx(Icon, {
|
|
345
|
+
source: CancelMinor,
|
|
346
|
+
size: "sm",
|
|
347
|
+
color: "subdued"
|
|
348
|
+
})
|
|
349
|
+
})
|
|
350
|
+
]
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
export default ToastNotification;
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
function _array_like_to_array(arr, len) {
|
|
3
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
4
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
5
|
+
return arr2;
|
|
6
|
+
}
|
|
7
|
+
function _array_with_holes(arr) {
|
|
8
|
+
if (Array.isArray(arr)) return arr;
|
|
9
|
+
}
|
|
10
|
+
function _array_without_holes(arr) {
|
|
11
|
+
if (Array.isArray(arr)) return _array_like_to_array(arr);
|
|
12
|
+
}
|
|
13
|
+
function _iterable_to_array(iter) {
|
|
14
|
+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
15
|
+
}
|
|
16
|
+
function _iterable_to_array_limit(arr, i) {
|
|
17
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
18
|
+
if (_i == null) return;
|
|
19
|
+
var _arr = [];
|
|
20
|
+
var _n = true;
|
|
21
|
+
var _d = false;
|
|
22
|
+
var _s, _e;
|
|
23
|
+
try {
|
|
24
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
25
|
+
_arr.push(_s.value);
|
|
26
|
+
if (i && _arr.length === i) break;
|
|
27
|
+
}
|
|
28
|
+
} catch (err) {
|
|
29
|
+
_d = true;
|
|
30
|
+
_e = err;
|
|
31
|
+
} finally{
|
|
32
|
+
try {
|
|
33
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
34
|
+
} finally{
|
|
35
|
+
if (_d) throw _e;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return _arr;
|
|
39
|
+
}
|
|
40
|
+
function _non_iterable_rest() {
|
|
41
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
42
|
+
}
|
|
43
|
+
function _non_iterable_spread() {
|
|
44
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
45
|
+
}
|
|
46
|
+
function _sliced_to_array(arr, i) {
|
|
47
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
48
|
+
}
|
|
49
|
+
function _to_consumable_array(arr) {
|
|
50
|
+
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
51
|
+
}
|
|
52
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
53
|
+
if (!o) return;
|
|
54
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
55
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
56
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
57
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
58
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
59
|
+
}
|
|
60
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
61
|
+
import { createContext, useContext, useState, useCallback, useEffect } from "react";
|
|
62
|
+
import { flushSync } from "react-dom";
|
|
63
|
+
import { createPortal } from "react-dom";
|
|
64
|
+
import ToastNotification from "./ToastNotification.js";
|
|
65
|
+
import { useFrame } from "./Frame.js";
|
|
66
|
+
import Stack from "./Stack.js";
|
|
67
|
+
var ToastContext = /*#__PURE__*/ createContext(null);
|
|
68
|
+
/**
|
|
69
|
+
* Hook to access the toast notification system.
|
|
70
|
+
* Must be used within a ToastProvider.
|
|
71
|
+
*
|
|
72
|
+
* @returns {Object} Toast methods
|
|
73
|
+
* @returns {Function} returns.show - Show a toast notification
|
|
74
|
+
* @returns {Function} returns.dismiss - Dismiss a specific toast by ID
|
|
75
|
+
* @returns {Function} returns.dismissAll - Dismiss all toasts
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* const toast = useToast();
|
|
79
|
+
* toast.show({
|
|
80
|
+
* title: "Success!",
|
|
81
|
+
* subtitle: "Your changes have been saved",
|
|
82
|
+
* prefix: <Icon source={TickMinor} color="success" />
|
|
83
|
+
* });
|
|
84
|
+
*/ export var useToast = function() {
|
|
85
|
+
var context = useContext(ToastContext);
|
|
86
|
+
if (!context) {
|
|
87
|
+
// Return a no-op function if used outside provider
|
|
88
|
+
return {
|
|
89
|
+
show: function() {
|
|
90
|
+
console.warn("useToast must be used within a ToastProvider");
|
|
91
|
+
},
|
|
92
|
+
dismiss: function() {},
|
|
93
|
+
dismissAll: function() {}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return context;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* ToastProvider component that manages multiple toast notifications.
|
|
100
|
+
* Handles stacking and positioning of multiple toasts.
|
|
101
|
+
*
|
|
102
|
+
* @component
|
|
103
|
+
* @param {Object} props - The component props.
|
|
104
|
+
* @param {React.ReactNode} props.children - Child components that can use the toast system.
|
|
105
|
+
* @param {'topRight' | 'topLeft' | 'topCenter' | 'bottomRight' | 'bottomLeft' | 'bottomCenter'} [props.defaultPosition='topRight'] - Default position for toasts.
|
|
106
|
+
* @param {number} [props.maxToasts=5] - Maximum number of toasts to display at once.
|
|
107
|
+
* @param {number} [props.stackGap=2] - Gap in pixels between stacked toasts.
|
|
108
|
+
* @returns {JSX.Element} The provider component.
|
|
109
|
+
*/ function ToastProvider(param) {
|
|
110
|
+
var children = param.children, _param_defaultPosition = param.defaultPosition, defaultPosition = _param_defaultPosition === void 0 ? "topRight" : _param_defaultPosition, _param_maxToasts = param.maxToasts, maxToasts = _param_maxToasts === void 0 ? 5 : _param_maxToasts, _param_stackGap = param.stackGap, stackGap = _param_stackGap === void 0 ? 2 : _param_stackGap;
|
|
111
|
+
var _useState = _sliced_to_array(useState([]), 2), toasts = _useState[0], setToasts = _useState[1];
|
|
112
|
+
var _useState1 = _sliced_to_array(useState([]), 2), dismissingToasts = _useState1[0], setDismissingToasts = _useState1[1];
|
|
113
|
+
var _useState2 = _sliced_to_array(useState(null), 2), container = _useState2[0], setContainer = _useState2[1];
|
|
114
|
+
var paneIsOpen = useFrame().paneIsOpen;
|
|
115
|
+
// Create portal container on mount
|
|
116
|
+
useEffect(function() {
|
|
117
|
+
if (typeof window !== "undefined") {
|
|
118
|
+
var div = document.createElement("div");
|
|
119
|
+
div.className = "Litho-ToastProvider";
|
|
120
|
+
div.setAttribute("data-toast-container", "true");
|
|
121
|
+
document.body.appendChild(div);
|
|
122
|
+
setContainer(div);
|
|
123
|
+
return function() {
|
|
124
|
+
if (document.body.contains(div)) {
|
|
125
|
+
document.body.removeChild(div);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}, []);
|
|
130
|
+
var show = useCallback(function() {
|
|
131
|
+
var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
132
|
+
var prefix = options.prefix, icon = options.icon, title = options.title, subtitle = options.subtitle, _options_duration = options.duration, duration = _options_duration === void 0 ? 2000 : _options_duration, _options_position = options.position, position = _options_position === void 0 ? defaultPosition : _options_position, _options_dismissible = options.dismissible, dismissible = _options_dismissible === void 0 ? true : _options_dismissible, onDismiss = options.onDismiss, onClick = options.onClick, id = options.id;
|
|
133
|
+
if (!title) {
|
|
134
|
+
console.warn("Toast requires a title");
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
var toastId = id || "toast-".concat(Date.now(), "-").concat(Math.random().toString(36).substr(2, 9));
|
|
138
|
+
setToasts(function(prev) {
|
|
139
|
+
// Limit number of toasts
|
|
140
|
+
var newToasts = _to_consumable_array(prev).concat([
|
|
141
|
+
{
|
|
142
|
+
id: toastId,
|
|
143
|
+
prefix: prefix,
|
|
144
|
+
icon: icon,
|
|
145
|
+
title: title,
|
|
146
|
+
subtitle: subtitle,
|
|
147
|
+
duration: duration,
|
|
148
|
+
position: position,
|
|
149
|
+
dismissible: dismissible,
|
|
150
|
+
onDismiss: onDismiss,
|
|
151
|
+
onClick: onClick
|
|
152
|
+
}
|
|
153
|
+
]);
|
|
154
|
+
return newToasts.slice(-maxToasts);
|
|
155
|
+
});
|
|
156
|
+
return toastId;
|
|
157
|
+
}, [
|
|
158
|
+
defaultPosition,
|
|
159
|
+
maxToasts
|
|
160
|
+
]);
|
|
161
|
+
var dismiss = useCallback(function(id) {
|
|
162
|
+
// Move toast to dismissing state for exit animation, remove from active toasts immediately
|
|
163
|
+
flushSync(function() {
|
|
164
|
+
setToasts(function(prev) {
|
|
165
|
+
var toast = prev.find(function(t) {
|
|
166
|
+
return t.id === id;
|
|
167
|
+
});
|
|
168
|
+
if (toast) {
|
|
169
|
+
// Add to dismissing toasts for exit animation
|
|
170
|
+
setDismissingToasts(function(prevDismissing) {
|
|
171
|
+
// Avoid duplicates
|
|
172
|
+
if (prevDismissing.find(function(t) {
|
|
173
|
+
return t.id === id;
|
|
174
|
+
})) {
|
|
175
|
+
return prevDismissing;
|
|
176
|
+
}
|
|
177
|
+
return _to_consumable_array(prevDismissing).concat([
|
|
178
|
+
toast
|
|
179
|
+
]);
|
|
180
|
+
});
|
|
181
|
+
// Remove from active toasts immediately so others slide up
|
|
182
|
+
return prev.filter(function(t) {
|
|
183
|
+
return t.id !== id;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return prev;
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
// Clean up dismissing toast after animation completes
|
|
190
|
+
setTimeout(function() {
|
|
191
|
+
setDismissingToasts(function(prev) {
|
|
192
|
+
return prev.filter(function(t) {
|
|
193
|
+
return t.id !== id;
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}, 300); // Match transition duration
|
|
197
|
+
}, []);
|
|
198
|
+
var dismissAll = useCallback(function() {
|
|
199
|
+
setToasts([]);
|
|
200
|
+
}, []);
|
|
201
|
+
var handleToastDismiss = useCallback(function(id, onDismiss) {
|
|
202
|
+
// Remove from state immediately so other toasts can slide up
|
|
203
|
+
dismiss(id);
|
|
204
|
+
// Call onDismiss callback after a brief delay to allow exit animation to start
|
|
205
|
+
if (onDismiss) {
|
|
206
|
+
setTimeout(function() {
|
|
207
|
+
onDismiss();
|
|
208
|
+
}, 0);
|
|
209
|
+
}
|
|
210
|
+
}, [
|
|
211
|
+
dismiss
|
|
212
|
+
]);
|
|
213
|
+
// Group toasts by position
|
|
214
|
+
var toastsByPosition = toasts.reduce(function(acc, toast) {
|
|
215
|
+
var position = toast.position || defaultPosition;
|
|
216
|
+
if (!acc[position]) {
|
|
217
|
+
acc[position] = [];
|
|
218
|
+
}
|
|
219
|
+
acc[position].push(toast);
|
|
220
|
+
return acc;
|
|
221
|
+
}, {});
|
|
222
|
+
if (!container) {
|
|
223
|
+
return /*#__PURE__*/ _jsx(_Fragment, {
|
|
224
|
+
children: children
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return /*#__PURE__*/ _jsxs(ToastContext.Provider, {
|
|
228
|
+
value: {
|
|
229
|
+
show: show,
|
|
230
|
+
dismiss: dismiss,
|
|
231
|
+
dismissAll: dismissAll
|
|
232
|
+
},
|
|
233
|
+
children: [
|
|
234
|
+
children,
|
|
235
|
+
/*#__PURE__*/ createPortal(/*#__PURE__*/ _jsxs(_Fragment, {
|
|
236
|
+
children: [
|
|
237
|
+
dismissingToasts.map(function(toast) {
|
|
238
|
+
var position = toast.position || defaultPosition;
|
|
239
|
+
var isTop = position.startsWith("top");
|
|
240
|
+
var isBottom = position.startsWith("bottom");
|
|
241
|
+
var dismissingStyle = {
|
|
242
|
+
position: "fixed",
|
|
243
|
+
zIndex: 9998
|
|
244
|
+
};
|
|
245
|
+
if (isTop) {
|
|
246
|
+
dismissingStyle.top = "calc(12px + var(--litho-header-height))";
|
|
247
|
+
} else if (isBottom) {
|
|
248
|
+
dismissingStyle.bottom = "12px";
|
|
249
|
+
}
|
|
250
|
+
if (position.includes("Right")) {
|
|
251
|
+
if (paneIsOpen) {
|
|
252
|
+
dismissingStyle.right = "calc(12px + var(--litho-pane-width))";
|
|
253
|
+
} else {
|
|
254
|
+
dismissingStyle.right = "12px";
|
|
255
|
+
}
|
|
256
|
+
} else if (position.includes("Left")) {
|
|
257
|
+
dismissingStyle.left = "12px";
|
|
258
|
+
} else if (position.includes("Center")) {
|
|
259
|
+
dismissingStyle.left = "50%";
|
|
260
|
+
dismissingStyle.transform = "translateX(-50%)";
|
|
261
|
+
}
|
|
262
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
263
|
+
style: dismissingStyle,
|
|
264
|
+
className: "Litho-ToastProvider__DismissingToast",
|
|
265
|
+
children: /*#__PURE__*/ _jsx(ToastNotification, {
|
|
266
|
+
prefix: toast.prefix,
|
|
267
|
+
icon: toast.icon,
|
|
268
|
+
title: toast.title,
|
|
269
|
+
subtitle: toast.subtitle,
|
|
270
|
+
duration: 0,
|
|
271
|
+
position: toast.position || defaultPosition,
|
|
272
|
+
dismissible: false,
|
|
273
|
+
fixed: false,
|
|
274
|
+
isDismissing: true,
|
|
275
|
+
onClick: toast.onClick,
|
|
276
|
+
onDismiss: function() {}
|
|
277
|
+
})
|
|
278
|
+
}, "dismissing-".concat(toast.id));
|
|
279
|
+
}),
|
|
280
|
+
Object.entries(toastsByPosition).map(function(param) {
|
|
281
|
+
var _param = _sliced_to_array(param, 2), position = _param[0], positionToasts = _param[1];
|
|
282
|
+
var isTop = position.startsWith("top");
|
|
283
|
+
var isBottom = position.startsWith("bottom");
|
|
284
|
+
var isCenter = position.includes("Center");
|
|
285
|
+
var containerStyle = {
|
|
286
|
+
position: "fixed",
|
|
287
|
+
zIndex: 9999
|
|
288
|
+
};
|
|
289
|
+
if (isTop) {
|
|
290
|
+
containerStyle.top = "calc(12px + var(--litho-header-height))"; // top-4
|
|
291
|
+
} else if (isBottom) {
|
|
292
|
+
containerStyle.bottom = "12px"; // bottom-4
|
|
293
|
+
}
|
|
294
|
+
if (position.includes("Right")) {
|
|
295
|
+
// Adjust right position when pane is open to avoid covering the pane
|
|
296
|
+
if (paneIsOpen) {
|
|
297
|
+
containerStyle.right = "calc(12px + var(--litho-pane-width))";
|
|
298
|
+
} else {
|
|
299
|
+
containerStyle.right = "12px";
|
|
300
|
+
}
|
|
301
|
+
} else if (position.includes("Left")) {
|
|
302
|
+
containerStyle.left = "12px";
|
|
303
|
+
} else if (position.includes("Center")) {
|
|
304
|
+
containerStyle.left = "50%";
|
|
305
|
+
containerStyle.transform = "translateX(-50%)";
|
|
306
|
+
}
|
|
307
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
308
|
+
style: containerStyle,
|
|
309
|
+
className: "Litho-ToastProvider__Container",
|
|
310
|
+
children: /*#__PURE__*/ _jsx(Stack, {
|
|
311
|
+
gap: stackGap,
|
|
312
|
+
children: positionToasts.map(function(toast, index) {
|
|
313
|
+
return /*#__PURE__*/ _jsx(ToastNotification, {
|
|
314
|
+
prefix: toast.prefix,
|
|
315
|
+
icon: toast.icon,
|
|
316
|
+
title: toast.title,
|
|
317
|
+
subtitle: toast.subtitle,
|
|
318
|
+
duration: toast.duration,
|
|
319
|
+
position: toast.position || defaultPosition,
|
|
320
|
+
dismissible: toast.dismissible,
|
|
321
|
+
fixed: false,
|
|
322
|
+
onClick: toast.onClick,
|
|
323
|
+
onDismiss: function() {
|
|
324
|
+
return handleToastDismiss(toast.id, toast.onDismiss);
|
|
325
|
+
}
|
|
326
|
+
}, toast.id);
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
}, position);
|
|
330
|
+
})
|
|
331
|
+
]
|
|
332
|
+
}), container)
|
|
333
|
+
]
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
export default ToastProvider;
|