@heymantle/litho 0.0.12 → 0.0.14

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.
Files changed (33) hide show
  1. package/dist/cjs/components/DropZone.js +4 -0
  2. package/dist/cjs/components/Navigation.js +4 -3
  3. package/dist/cjs/components/Popover.js +33 -5
  4. package/dist/cjs/components/Select.js +4 -0
  5. package/dist/cjs/components/TabNavigation.js +65 -11
  6. package/dist/cjs/components/TextField.js +4 -2
  7. package/dist/cjs/components/ToastNotification.js +368 -0
  8. package/dist/cjs/components/ToastProvider.js +342 -0
  9. package/dist/cjs/index.js +11 -0
  10. package/dist/esm/components/DropZone.js +5 -1
  11. package/dist/esm/components/Navigation.js +4 -3
  12. package/dist/esm/components/Popover.js +33 -5
  13. package/dist/esm/components/Select.js +5 -1
  14. package/dist/esm/components/TabNavigation.js +65 -11
  15. package/dist/esm/components/TextField.js +4 -2
  16. package/dist/esm/components/ToastNotification.js +353 -0
  17. package/dist/esm/components/ToastProvider.js +336 -0
  18. package/dist/esm/index.js +2 -0
  19. package/dist/types/components/DropZone.d.ts.map +1 -1
  20. package/dist/types/components/Navigation.d.ts +1 -0
  21. package/dist/types/components/Navigation.d.ts.map +1 -1
  22. package/dist/types/components/Popover.d.ts.map +1 -1
  23. package/dist/types/components/Select.d.ts.map +1 -1
  24. package/dist/types/components/TabNavigation.d.ts.map +1 -1
  25. package/dist/types/components/TextField.d.ts +2 -0
  26. package/dist/types/components/TextField.d.ts.map +1 -1
  27. package/dist/types/components/ToastNotification.d.ts +36 -0
  28. package/dist/types/components/ToastNotification.d.ts.map +1 -0
  29. package/dist/types/components/ToastProvider.d.ts +21 -0
  30. package/dist/types/components/ToastProvider.d.ts.map +1 -0
  31. package/dist/types/index.d.ts +2 -0
  32. package/index.css +3 -0
  33. package/package.json +2 -2
@@ -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;