goey-toast 0.3.0 → 0.4.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/README.md CHANGED
@@ -23,7 +23,8 @@
23
23
  - Dismiss by type filter: `gooeyToast.dismiss({ type: 'error' })`
24
24
  - Dark mode and RTL layout support
25
25
  - Animation presets: smooth, bouncy, subtle, snappy
26
- - Timestamp display on expanded toasts
26
+ - Timestamp display on expanded toasts with optional `showTimestamp` toggle
27
+ - Close button with configurable position (`top-left` / `top-right`)
27
28
  - Countdown progress bar with hover-pause and re-expand
28
29
  - Keyboard dismiss (Escape) and swipe-to-dismiss on mobile
29
30
  - Toast queue with configurable overflow strategy
@@ -164,6 +165,7 @@ Options passed as the second argument to `gooeyToast()` and type-specific method
164
165
  | `timing` | `GooeyToastTimings` | Animation timing overrides |
165
166
  | `spring` | `boolean` | Enable spring/bounce animations (default `true`) |
166
167
  | `bounce` | `number` | Spring intensity from `0.05` (subtle) to `0.8` (dramatic), default `0.4` |
168
+ | `showTimestamp` | `boolean` | Show/hide timestamp in toast header/body (default `true`) |
167
169
  | `showProgress`| `boolean` | Show countdown progress bar |
168
170
  | `onDismiss` | `(id) => void` | Called when toast is dismissed (any reason) |
169
171
  | `onAutoClose` | `(id) => void` | Called only on timer-based auto-dismiss |
@@ -216,6 +218,7 @@ Props for the `<GooeyToaster />` component.
216
218
  | `bounce` | `number` | `0.4` | Spring intensity: `0.05` (subtle) to `0.8` (dramatic) |
217
219
  | `preset` | `AnimationPresetName` | -- | Animation preset for all toasts |
218
220
  | `closeOnEscape` | `boolean` | `true` | Dismiss most recent toast on Escape key |
221
+ | `closeButton` | `boolean \| 'top-left' \| 'top-right'` | `false` | Show close button on hover |
219
222
  | `showProgress` | `boolean` | `false` | Show countdown progress bar on all toasts |
220
223
  | `maxQueue` | `number` | `Infinity` | Maximum queued toasts |
221
224
  | `queueOverflow` | `'drop-oldest' \| 'drop-newest'` | `'drop-oldest'` | Queue overflow strategy |
@@ -422,6 +425,26 @@ Press **Escape** to dismiss the most recent toast. Enabled by default; disable w
422
425
 
423
426
  On mobile, swipe toasts to dismiss them. Enabled by default; disable with `swipeToDismiss={false}`.
424
427
 
428
+ ### Close Button
429
+
430
+ Show a close button on hover. Position it `top-left` (default) or `top-right`:
431
+
432
+ ```tsx
433
+ <GooeyToaster closeButton />
434
+ <GooeyToaster closeButton="top-left" />
435
+ <GooeyToaster closeButton="top-right" />
436
+ ```
437
+
438
+ The close button inherits the toast's border and fill color styling. Hidden during the loading phase of promise toasts.
439
+
440
+ ### Hiding Timestamps
441
+
442
+ Hide the timestamp from toasts:
443
+
444
+ ```tsx
445
+ gooeyToast.success('Saved', { showTimestamp: false })
446
+ ```
447
+
425
448
  ## Exports
426
449
 
427
450
  ```ts
@@ -458,6 +481,10 @@ goey-toast works in all modern browsers that support:
458
481
  - ResizeObserver
459
482
  - `framer-motion` (Chrome, Firefox, Safari, Edge)
460
483
 
484
+ ## See Also
485
+
486
+ - **[gooey-search-tabs](https://github.com/anl331/gooey-search-tabs)** — A morphing search bar with animated tab navigation for React. [Live Demo](https://gooey-search-tabs.vercel.app)
487
+
461
488
  ## License
462
489
 
463
490
  [MIT](./LICENSE)
package/dist/index.cjs CHANGED
@@ -86,6 +86,13 @@ function setGooeyShowProgress(show) {
86
86
  function getGooeyShowProgress() {
87
87
  return _showProgress;
88
88
  }
89
+ var _closeButton = false;
90
+ function setGooeyCloseButton(value) {
91
+ _closeButton = value;
92
+ }
93
+ function getGooeyCloseButton() {
94
+ return _closeButton;
95
+ }
89
96
  var _containerHovered = false;
90
97
  var _hoverSubs = /* @__PURE__ */ new Set();
91
98
  function setContainerHovered(hovered) {
@@ -282,7 +289,9 @@ var styles = {
282
289
  progressWarning: "gooey-progressWarning",
283
290
  progressInfo: "gooey-progressInfo",
284
291
  progressPaused: "gooey-progressPaused",
285
- timestamp: "gooey-timestamp"
292
+ timestamp: "gooey-timestamp",
293
+ closeButton: "gooey-closeButton",
294
+ closeButtonRight: "gooey-closeButtonRight"
286
295
  };
287
296
  var useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
288
297
  var phaseIconMap = {
@@ -536,10 +545,13 @@ var GooeyToast = ({
536
545
  preset,
537
546
  spring: springProp,
538
547
  bounce: bounceProp,
548
+ showTimestamp = true,
539
549
  showProgress: showProgressProp,
540
550
  toastId
541
551
  }) => {
542
552
  const theme = getGooeyTheme();
553
+ const closeButtonSetting = getGooeyCloseButton();
554
+ const showCloseButton = closeButtonSetting !== false;
543
555
  const fillColor = fillColorProp ?? (theme === "dark" ? "#1a1a1a" : "#ffffff");
544
556
  const position = getGooeyPosition();
545
557
  const dir = getGooeyDir();
@@ -1204,6 +1216,30 @@ var GooeyToast = ({
1204
1216
  )
1205
1217
  }
1206
1218
  ),
1219
+ showCloseButton && effectivePhase !== "loading" && /* @__PURE__ */ jsxRuntime.jsx(
1220
+ "button",
1221
+ {
1222
+ className: `${styles.closeButton}${(isRight ? closeButtonSetting !== "top-right" : closeButtonSetting === "top-right") ? ` ${styles.closeButtonRight}` : ""}`,
1223
+ "aria-label": "Close toast",
1224
+ type: "button",
1225
+ style: {
1226
+ background: fillColor,
1227
+ borderColor: borderColor || "transparent",
1228
+ borderWidth: borderColor ? borderWidth ?? 1.5 : 0,
1229
+ boxShadow: borderColor ? "none" : "0 1px 4px rgba(0, 0, 0, 0.2)",
1230
+ ...isCenter && closeButtonSetting !== "top-right" ? { top: 6, left: -1 } : {}
1231
+ },
1232
+ onClick: (e) => {
1233
+ e.stopPropagation();
1234
+ const id = toastId;
1235
+ if (id != null) sonner.toast.dismiss(id);
1236
+ },
1237
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1238
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1239
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1240
+ ] })
1241
+ }
1242
+ ),
1207
1243
  /* @__PURE__ */ jsxRuntime.jsxs(
1208
1244
  "div",
1209
1245
  {
@@ -1213,9 +1249,9 @@ var GooeyToast = ({
1213
1249
  children: [
1214
1250
  /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: headerRef, className: `${styles.header} ${titleColorMap[effectivePhase]}${classNames?.header ? ` ${classNames.header}` : ""}`, children: [
1215
1251
  iconAndTitle,
1216
- !hasDescription && !hasAction && !actionSuccess && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.timestamp, children: timestampStr })
1252
+ !hasDescription && !hasAction && !actionSuccess && showTimestamp && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.timestamp, children: timestampStr })
1217
1253
  ] }),
1218
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showBody && hasDescription && !dismissing && /* @__PURE__ */ jsxRuntime.jsxs(
1254
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showBody && hasDescription && !dismissing && /* @__PURE__ */ jsxRuntime.jsx(
1219
1255
  framerMotion.motion.div,
1220
1256
  {
1221
1257
  className: `${styles.description}${classNames?.description ? ` ${classNames.description}` : ""}`,
@@ -1224,14 +1260,14 @@ var GooeyToast = ({
1224
1260
  animate: { opacity: 1 },
1225
1261
  exit: { opacity: 0 },
1226
1262
  transition: prefersReducedMotion ? { duration: 0.01 } : { duration: 0.35, ease: [0.4, 0, 0.2, 1] },
1227
- children: [
1228
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.timestamp, style: { float: "right", marginLeft: 10, marginTop: 3, paddingLeft: 0 }, children: timestampStr }),
1229
- effectiveDescription
1230
- ]
1263
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: "10px" }, children: [
1264
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, minWidth: 0 }, children: effectiveDescription }),
1265
+ showTimestamp && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.timestamp, children: timestampStr })
1266
+ ] })
1231
1267
  },
1232
1268
  "description"
1233
1269
  ) }),
1234
- /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showBody && !hasDescription && hasAction && !dismissing && /* @__PURE__ */ jsxRuntime.jsx(
1270
+ /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: showBody && !hasDescription && hasAction && !dismissing && showTimestamp && /* @__PURE__ */ jsxRuntime.jsx(
1235
1271
  framerMotion.motion.div,
1236
1272
  {
1237
1273
  className: styles.timestamp,
@@ -1397,22 +1433,23 @@ function GooeyToastWrapper({
1397
1433
  spring,
1398
1434
  bounce,
1399
1435
  showProgress,
1436
+ showTimestamp: initialShowTimestamp,
1400
1437
  toastId,
1401
- activeId,
1402
1438
  onDismiss,
1403
1439
  onAutoClose
1404
1440
  }) {
1405
1441
  react.useEffect(() => {
1406
1442
  if (onDismiss || onAutoClose) {
1407
- _toastCallbacks.set(activeId, { onDismiss, onAutoClose });
1443
+ _toastCallbacks.set(toastId, { onDismiss, onAutoClose });
1408
1444
  }
1409
- }, [activeId, onDismiss, onAutoClose]);
1445
+ }, [toastId, onDismiss, onAutoClose]);
1410
1446
  const [title, setTitle] = react.useState(initialTitle);
1411
1447
  const [type, setType] = react.useState(initialType);
1412
1448
  const [phase, setPhase] = react.useState(initialPhase);
1413
1449
  const [description, setDescription] = react.useState(initialDescription);
1414
1450
  const [action, setAction] = react.useState(initialAction);
1415
1451
  const [currentIcon, setCurrentIcon] = react.useState(icon);
1452
+ const [showTimestamp, setShowTimestamp] = react.useState(initialShowTimestamp ?? true);
1416
1453
  react.useEffect(() => {
1417
1454
  const handleUpdate = (opts) => {
1418
1455
  if (opts.title !== void 0) setTitle(opts.title);
@@ -1423,22 +1460,23 @@ function GooeyToastWrapper({
1423
1460
  }
1424
1461
  if (opts.action !== void 0) setAction(opts.action);
1425
1462
  if ("icon" in opts) setCurrentIcon(opts.icon ?? void 0);
1463
+ if (opts.showTimestamp !== void 0) setShowTimestamp(opts.showTimestamp);
1426
1464
  };
1427
- _toastUpdateListeners.set(activeId, handleUpdate);
1465
+ _toastUpdateListeners.set(toastId, handleUpdate);
1428
1466
  return () => {
1429
- _toastUpdateListeners.delete(activeId);
1467
+ _toastUpdateListeners.delete(toastId);
1430
1468
  };
1431
- }, [activeId]);
1469
+ }, [toastId]);
1432
1470
  const mountedRef = react.useRef(true);
1433
1471
  react.useEffect(() => {
1434
1472
  mountedRef.current = true;
1435
1473
  return () => {
1436
1474
  mountedRef.current = false;
1437
1475
  setTimeout(() => {
1438
- if (!mountedRef.current) _onToastDismissed(activeId);
1476
+ if (!mountedRef.current) _onToastDismissed(toastId);
1439
1477
  }, 100);
1440
1478
  };
1441
- }, [activeId]);
1479
+ }, [toastId]);
1442
1480
  return /* @__PURE__ */ jsxRuntime.jsx(ToastErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx(
1443
1481
  GooeyToast,
1444
1482
  {
@@ -1457,6 +1495,7 @@ function GooeyToastWrapper({
1457
1495
  spring,
1458
1496
  bounce,
1459
1497
  showProgress,
1498
+ showTimestamp,
1460
1499
  toastId
1461
1500
  }
1462
1501
  ) });
@@ -1529,7 +1568,9 @@ function PromiseToastWrapper({
1529
1568
  timing: data.timing,
1530
1569
  preset: data.preset,
1531
1570
  spring: data.spring,
1532
- bounce: data.bounce
1571
+ bounce: data.bounce,
1572
+ showTimestamp: data.showTimestamp ?? true,
1573
+ toastId
1533
1574
  }
1534
1575
  ) });
1535
1576
  }
@@ -1558,8 +1599,8 @@ function createGooeyToast(title, type, options) {
1558
1599
  spring: options?.spring,
1559
1600
  bounce: options?.bounce,
1560
1601
  showProgress: options?.showProgress,
1561
- toastId: hasExpandedContent ? toastId : void 0,
1562
- activeId: toastId,
1602
+ showTimestamp: options?.showTimestamp,
1603
+ toastId,
1563
1604
  onDismiss: options?.onDismiss,
1564
1605
  onAutoClose: options?.onAutoClose
1565
1606
  }
@@ -1776,6 +1817,9 @@ function GooeyToaster({
1776
1817
  react.useEffect(() => {
1777
1818
  setGooeyShowProgress(showProgress);
1778
1819
  }, [showProgress]);
1820
+ react.useEffect(() => {
1821
+ setGooeyCloseButton(closeButton ?? false);
1822
+ }, [closeButton]);
1779
1823
  react.useEffect(() => {
1780
1824
  let expandObs = null;
1781
1825
  let currentOl = null;
@@ -1845,7 +1889,7 @@ function GooeyToaster({
1845
1889
  theme,
1846
1890
  toastOptions: { unstyled: true, ...toastOptions },
1847
1891
  expand,
1848
- closeButton,
1892
+ closeButton: false,
1849
1893
  richColors,
1850
1894
  visibleToasts: 99,
1851
1895
  dir
@@ -1855,8 +1899,10 @@ function GooeyToaster({
1855
1899
  ] });
1856
1900
  }
1857
1901
 
1902
+ exports.GoeyToaster = GooeyToaster;
1858
1903
  exports.GooeyToaster = GooeyToaster;
1859
1904
  exports.animationPresets = animationPresets;
1905
+ exports.goeyToast = gooeyToast;
1860
1906
  exports.gooeyToast = gooeyToast;
1861
1907
  //# sourceMappingURL=index.cjs.map
1862
1908
  //# sourceMappingURL=index.cjs.map