@xsolla/xui-toast 0.158.0 → 0.160.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/web/index.js CHANGED
@@ -311,6 +311,8 @@ var Text = ({
311
311
  className,
312
312
  id,
313
313
  role,
314
+ testID,
315
+ "data-testid": dataTestId,
314
316
  numberOfLines: _numberOfLines,
315
317
  ...props
316
318
  }) => {
@@ -321,7 +323,8 @@ var Text = ({
321
323
  style,
322
324
  className,
323
325
  id,
324
- role
326
+ role,
327
+ "data-testid": dataTestId || testID
325
328
  }
326
329
  );
327
330
  };
@@ -345,51 +348,110 @@ var StyledIcon = (0, import_styled_components3.default)(FilteredDiv2)`
345
348
  stroke: currentColor;
346
349
  }
347
350
  `;
348
- var Icon = ({ children, ...props }) => {
349
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StyledIcon, { ...props, children });
351
+ var Icon = ({
352
+ children,
353
+ testID,
354
+ "data-testid": dataTestId,
355
+ ...props
356
+ }) => {
357
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StyledIcon, { "data-testid": dataTestId || testID, ...props, children });
350
358
  };
351
359
 
352
360
  // src/Toast.tsx
353
361
  var import_xui_core = require("@xsolla/xui-core");
354
- var import_xui_icons = require("@xsolla/xui-icons");
362
+ var import_xui_icons_base = require("@xsolla/xui-icons-base");
355
363
  var import_jsx_runtime4 = require("react/jsx-runtime");
356
- var getDefaultIcon = (variant, size, color) => {
357
- switch (variant) {
358
- case "success":
359
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_xui_icons.Check, { size, color });
360
- case "info":
361
- case "warning":
362
- case "error":
363
- default:
364
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_xui_icons.AlertCircle, { size, color });
365
- }
364
+ var TOAST_COLORS = {
365
+ alert: "#ff553d",
366
+ success: "#47d94a",
367
+ neutral: "#7899a1",
368
+ warning: "#ffb95c"
366
369
  };
367
- var getIconColor = (variant, colors) => {
368
- switch (variant) {
370
+ var DEFAULT_COLOR_SCHEME = {
371
+ background: "#34474b",
372
+ text: "#ffffff",
373
+ descriptionText: "rgba(255, 255, 255, 0.7)",
374
+ divider: "rgba(255, 255, 255, 0.16)",
375
+ actionText: "#ffffff"
376
+ };
377
+ var getDefaultIcon = (type, size, color) => {
378
+ switch (type) {
369
379
  case "success":
370
- return colors.content.success.primary;
380
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
381
+ import_xui_icons_base.CheckCr,
382
+ {
383
+ variant: "solid",
384
+ size,
385
+ color,
386
+ "data-testid": "toast-icon-success"
387
+ }
388
+ );
389
+ case "neutral":
390
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
391
+ import_xui_icons_base.InfoCr,
392
+ {
393
+ variant: "solid",
394
+ size,
395
+ color,
396
+ "data-testid": "toast-icon-neutral"
397
+ }
398
+ );
371
399
  case "warning":
372
- return colors.content.warning.primary;
373
- case "error":
374
- return colors.content.alert.primary;
375
- case "info":
400
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
401
+ import_xui_icons_base.Warning,
402
+ {
403
+ variant: "solid",
404
+ size,
405
+ color,
406
+ "data-testid": "toast-icon-warning"
407
+ }
408
+ );
409
+ case "alert":
376
410
  default:
377
- return colors.content.inverse;
411
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
412
+ import_xui_icons_base.ExclamationMarkCr,
413
+ {
414
+ variant: "solid",
415
+ size,
416
+ color,
417
+ "data-testid": "toast-icon-alert"
418
+ }
419
+ );
378
420
  }
379
421
  };
380
422
  var ANIMATION_DURATION = 200;
423
+ var PROGRESS_HEIGHT = 4;
424
+ var TOAST_CONFIG = {
425
+ minHeight: 56,
426
+ paddingHorizontal: 16,
427
+ paddingVertical: 12,
428
+ borderRadius: 4,
429
+ gap: 12,
430
+ iconSize: 24,
431
+ closeButtonSize: 24,
432
+ closeIconSize: 20,
433
+ fontSize: 16,
434
+ lineHeight: 20,
435
+ maxWidth: 440
436
+ };
381
437
  var Toast = ({
382
438
  id,
383
- variant = "info",
384
- message,
385
- icon,
439
+ type = "neutral",
440
+ title,
441
+ description,
442
+ icon = true,
443
+ action,
444
+ showCloseButton = true,
445
+ progress = false,
386
446
  duration,
387
447
  onClose,
448
+ colorScheme = DEFAULT_COLOR_SCHEME,
449
+ testID,
388
450
  themeMode,
389
451
  themeProductContext
390
452
  }) => {
391
- const { theme } = (0, import_xui_core.useResolvedTheme)({ themeMode, themeProductContext });
392
- const config = theme.sizing.toast();
453
+ (0, import_xui_core.useResolvedTheme)({ themeMode, themeProductContext });
454
+ const config = TOAST_CONFIG;
393
455
  const [visible, setVisible] = (0, import_react3.useState)(false);
394
456
  const [dismissing, setDismissing] = (0, import_react3.useState)(false);
395
457
  (0, import_react3.useEffect)(() => {
@@ -406,63 +468,147 @@ var Toast = ({
406
468
  const timer = setTimeout(handleClose, duration);
407
469
  return () => clearTimeout(timer);
408
470
  }, [duration]);
409
- const iconColor = getIconColor(variant, theme.colors);
410
- const displayIcon = icon !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { size: config.iconSize, color: iconColor, children: icon }) : getDefaultIcon(variant, config.iconSize, iconColor);
471
+ const iconColor = TOAST_COLORS[type];
472
+ const shouldShowIcon = icon !== false;
473
+ const displayIcon = icon === true || icon === void 0 ? getDefaultIcon(type, config.iconSize, iconColor) : icon;
474
+ const renderedAction = (0, import_react3.isValidElement)(action) && typeof action.type !== "string" ? (0, import_react3.cloneElement)(action, {
475
+ size: "xs",
476
+ variant: "inverse",
477
+ background: false,
478
+ themeMode: "dark",
479
+ themeProductContext,
480
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: colorScheme.actionText }, children: action.props.children })
481
+ }) : action;
482
+ const hasAction = Boolean(action);
483
+ const hasCloseButton = showCloseButton && Boolean(onClose);
484
+ const hasActions = hasAction || hasCloseButton;
485
+ const role = type === "neutral" || type === "success" ? "status" : "alert";
486
+ const ariaLive = type === "neutral" || type === "success" ? "polite" : "assertive";
411
487
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
412
488
  Box,
413
489
  {
414
- backgroundColor: theme.colors.background.inverse,
490
+ testID,
491
+ backgroundColor: colorScheme.background,
415
492
  borderRadius: config.borderRadius,
416
- paddingHorizontal: config.paddingHorizontal,
417
- paddingVertical: config.paddingVertical,
418
493
  minHeight: config.minHeight,
419
494
  flexDirection: "row",
420
495
  alignItems: "center",
421
- gap: config.gap,
422
- width: "100%",
423
- role: "alert",
424
- "aria-live": "polite",
496
+ justifyContent: "center",
497
+ overflow: "hidden",
498
+ width: config.maxWidth,
499
+ maxWidth: "100%",
500
+ role,
501
+ "aria-live": ariaLive,
425
502
  "data-toast-id": id,
426
503
  style: {
427
504
  opacity: visible && !dismissing ? 1 : 0,
428
505
  transform: visible && !dismissing ? "translateY(0)" : "translateY(-8px)",
429
- transition: `opacity ${ANIMATION_DURATION}ms ease-out, transform ${ANIMATION_DURATION}ms ease-out`
506
+ transition: `opacity ${ANIMATION_DURATION}ms ease-out, transform ${ANIMATION_DURATION}ms ease-out`,
507
+ boxShadow: colorScheme.shadow,
508
+ backdropFilter: colorScheme.backdropFilter
430
509
  },
431
510
  children: [
432
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
511
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
433
512
  Box,
434
513
  {
435
- width: config.iconSize,
436
- height: config.iconSize,
514
+ flex: 1,
515
+ minHeight: config.minHeight,
516
+ minWidth: 0,
517
+ flexDirection: "row",
437
518
  alignItems: "center",
438
- justifyContent: "center",
439
- flexShrink: 0,
440
- children: displayIcon
519
+ gap: config.gap,
520
+ paddingHorizontal: config.paddingHorizontal,
521
+ paddingVertical: config.paddingVertical,
522
+ children: [
523
+ shouldShowIcon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
524
+ Box,
525
+ {
526
+ width: config.iconSize,
527
+ height: config.iconSize,
528
+ alignItems: "center",
529
+ justifyContent: "center",
530
+ flexShrink: 0,
531
+ children: icon === true || icon === void 0 ? displayIcon : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { size: config.iconSize, color: iconColor, children: displayIcon })
532
+ }
533
+ ),
534
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box, { flex: 1, minWidth: 0, gap: 2, children: [
535
+ title && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
536
+ Text,
537
+ {
538
+ color: colorScheme.text,
539
+ fontSize: config.fontSize,
540
+ lineHeight: config.lineHeight,
541
+ fontWeight: "500",
542
+ numberOfLines: 1,
543
+ children: title
544
+ }
545
+ ),
546
+ description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
547
+ Text,
548
+ {
549
+ color: colorScheme.descriptionText,
550
+ fontSize: 14,
551
+ lineHeight: 18,
552
+ numberOfLines: 1,
553
+ children: description
554
+ }
555
+ )
556
+ ] }),
557
+ hasActions && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
558
+ Box,
559
+ {
560
+ height: 32,
561
+ flexDirection: "row",
562
+ alignItems: "center",
563
+ gap: config.gap,
564
+ flexShrink: 0,
565
+ children: [
566
+ renderedAction,
567
+ hasAction && hasCloseButton && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
568
+ Box,
569
+ {
570
+ width: 1,
571
+ height: "100%",
572
+ backgroundColor: colorScheme.divider,
573
+ testID: "toast-divider"
574
+ }
575
+ ),
576
+ hasCloseButton && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
577
+ Box,
578
+ {
579
+ onPress: handleClose,
580
+ width: config.closeButtonSize,
581
+ height: config.closeButtonSize,
582
+ alignItems: "center",
583
+ justifyContent: "center",
584
+ flexShrink: 0,
585
+ role: "button",
586
+ "aria-label": "Dismiss notification",
587
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
588
+ import_xui_icons_base.Remove,
589
+ {
590
+ variant: "line",
591
+ size: config.closeIconSize,
592
+ color: colorScheme.text
593
+ }
594
+ )
595
+ }
596
+ )
597
+ ]
598
+ }
599
+ )
600
+ ]
441
601
  }
442
602
  ),
443
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box, { flex: 1, minWidth: 0, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
444
- Text,
445
- {
446
- color: theme.colors.content.inverse,
447
- fontSize: config.fontSize,
448
- lineHeight: config.lineHeight,
449
- fontWeight: "500",
450
- numberOfLines: 2,
451
- children: message
452
- }
453
- ) }),
454
- onClose && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
603
+ progress && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
455
604
  Box,
456
605
  {
457
- onPress: handleClose,
458
- width: config.closeButtonSize,
459
- height: config.closeButtonSize,
460
- alignItems: "center",
461
- justifyContent: "center",
462
- flexShrink: 0,
463
- role: "button",
464
- "aria-label": "Dismiss toast",
465
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_xui_icons.X, { size: config.closeIconSize, color: theme.colors.content.inverse })
606
+ position: "absolute",
607
+ left: 0,
608
+ right: 0,
609
+ bottom: 0,
610
+ height: PROGRESS_HEIGHT,
611
+ backgroundColor: iconColor
466
612
  }
467
613
  )
468
614
  ]
@@ -476,6 +622,10 @@ var import_react4 = require("react");
476
622
  var import_react_dom = __toESM(require("react-dom"));
477
623
  var import_xui_core2 = require("@xsolla/xui-core");
478
624
  var import_jsx_runtime5 = require("react/jsx-runtime");
625
+ var TOAST_GROUP_CONFIG = {
626
+ containerPadding: 12,
627
+ groupGap: 8
628
+ };
479
629
  var ToastGroup = (0, import_react4.memo)(
480
630
  ({
481
631
  toasts,
@@ -483,11 +633,12 @@ var ToastGroup = (0, import_react4.memo)(
483
633
  align = "center",
484
634
  maxWidth,
485
635
  onDismiss,
636
+ testID,
486
637
  themeMode,
487
638
  themeProductContext
488
639
  }) => {
489
- const { theme } = (0, import_xui_core2.useResolvedTheme)({ themeMode, themeProductContext });
490
- const config = theme.sizing.toast();
640
+ (0, import_xui_core2.useResolvedTheme)({ themeMode, themeProductContext });
641
+ const config = TOAST_GROUP_CONFIG;
491
642
  if (toasts.length === 0 || typeof document === "undefined") {
492
643
  return null;
493
644
  }
@@ -501,6 +652,7 @@ var ToastGroup = (0, import_react4.memo)(
501
652
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
502
653
  Box,
503
654
  {
655
+ testID,
504
656
  position: "fixed",
505
657
  left: 0,
506
658
  right: 0,
@@ -520,9 +672,13 @@ var ToastGroup = (0, import_react4.memo)(
520
672
  Toast,
521
673
  {
522
674
  id: toast.id,
523
- variant: toast.variant,
524
- message: toast.message,
675
+ type: toast.type,
676
+ title: toast.title,
677
+ description: toast.description,
525
678
  icon: toast.icon,
679
+ action: toast.action,
680
+ showCloseButton: toast.showCloseButton,
681
+ progress: toast.progress,
526
682
  duration: toast.duration,
527
683
  onClose: () => onDismiss(toast.id)
528
684
  },
@@ -556,7 +712,8 @@ var ToastProvider = ({
556
712
  position = "top",
557
713
  align = "center",
558
714
  defaultDuration = 5e3,
559
- maxWidth
715
+ maxWidth,
716
+ testID
560
717
  }) => {
561
718
  const [toasts, setToasts] = (0, import_react6.useState)([]);
562
719
  const dismissToast = (0, import_react6.useCallback)((id) => {
@@ -571,9 +728,13 @@ var ToastProvider = ({
571
728
  const duration = options.duration ?? defaultDuration;
572
729
  const newToast = {
573
730
  id,
574
- variant: options.variant ?? "info",
575
- message: options.message,
731
+ type: options.type ?? "neutral",
732
+ title: options.title,
733
+ description: options.description,
576
734
  icon: options.icon,
735
+ action: options.action,
736
+ showCloseButton: options.showCloseButton,
737
+ progress: options.progress,
577
738
  duration
578
739
  };
579
740
  setToasts((prev) => [...prev, newToast]);
@@ -599,7 +760,8 @@ var ToastProvider = ({
599
760
  position,
600
761
  align,
601
762
  maxWidth,
602
- onDismiss: dismissToast
763
+ onDismiss: dismissToast,
764
+ testID
603
765
  }
604
766
  )
605
767
  ] });
@@ -619,27 +781,27 @@ var useToast = () => {
619
781
  },
620
782
  [addToast]
621
783
  );
622
- const success = (0, import_react7.useCallback)(
623
- (message, options) => {
624
- return addToast({ ...options, message, variant: "success" });
784
+ const alert = (0, import_react7.useCallback)(
785
+ (options) => {
786
+ return addToast({ ...options, type: "alert" });
625
787
  },
626
788
  [addToast]
627
789
  );
628
- const info = (0, import_react7.useCallback)(
629
- (message, options) => {
630
- return addToast({ ...options, message, variant: "info" });
790
+ const success = (0, import_react7.useCallback)(
791
+ (options) => {
792
+ return addToast({ ...options, type: "success" });
631
793
  },
632
794
  [addToast]
633
795
  );
634
- const warning = (0, import_react7.useCallback)(
635
- (message, options) => {
636
- return addToast({ ...options, message, variant: "warning" });
796
+ const neutral = (0, import_react7.useCallback)(
797
+ (options) => {
798
+ return addToast({ ...options, type: "neutral" });
637
799
  },
638
800
  [addToast]
639
801
  );
640
- const error = (0, import_react7.useCallback)(
641
- (message, options) => {
642
- return addToast({ ...options, message, variant: "error" });
802
+ const warning = (0, import_react7.useCallback)(
803
+ (options) => {
804
+ return addToast({ ...options, type: "warning" });
643
805
  },
644
806
  [addToast]
645
807
  );
@@ -654,10 +816,10 @@ var useToast = () => {
654
816
  }, [dismissAllToasts]);
655
817
  return {
656
818
  toast,
819
+ alert,
657
820
  success,
658
- info,
821
+ neutral,
659
822
  warning,
660
- error,
661
823
  dismiss,
662
824
  dismissAll
663
825
  };