@xsolla/xui-toast 0.225.0 → 0.226.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Toast.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../../../foundation/primitives-native/src/Icon.tsx","../../src/ToastGroup.native.tsx","../../src/ToastProvider.tsx","../../src/ToastContext.tsx","../../src/useToast.ts"],"sourcesContent":["import React, {\n cloneElement,\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text, Icon } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme } from \"@xsolla/xui-core\";\nimport {\n CheckCr,\n ExclamationMarkCr,\n InfoCr,\n Remove,\n Warning as WarningIcon,\n} from \"@xsolla/xui-icons-base\";\nimport type { ToastColorScheme, ToastProps, ToastType } from \"./types\";\n\nconst TOAST_COLORS: Record<ToastType, string> = {\n alert: \"#ff553d\",\n success: \"#47d94a\",\n neutral: \"#7899a1\",\n warning: \"#ffb95c\",\n};\nconst DEFAULT_COLOR_SCHEME: ToastColorScheme = {\n background: \"#34474b\",\n text: \"#ffffff\",\n descriptionText: \"rgba(255, 255, 255, 0.7)\",\n divider: \"rgba(255, 255, 255, 0.16)\",\n actionText: \"#ffffff\",\n};\n\nconst getDefaultIcon = (\n type: ToastType,\n size: number,\n color: string\n): React.ReactNode => {\n switch (type) {\n case \"success\":\n return (\n <CheckCr\n variant=\"solid\"\n size={size}\n color={color}\n data-testid=\"toast-icon-success\"\n />\n );\n case \"neutral\":\n return (\n <InfoCr\n variant=\"solid\"\n size={size}\n color={color}\n data-testid=\"toast-icon-neutral\"\n />\n );\n case \"warning\":\n return (\n <WarningIcon\n variant=\"solid\"\n size={size}\n color={color}\n data-testid=\"toast-icon-warning\"\n />\n );\n case \"alert\":\n default:\n return (\n <ExclamationMarkCr\n variant=\"solid\"\n size={size}\n color={color}\n data-testid=\"toast-icon-alert\"\n />\n );\n }\n};\n\nconst ANIMATION_DURATION = 200;\nconst PROGRESS_HEIGHT = 4;\nconst TOAST_CONFIG = {\n minHeight: 56,\n paddingHorizontal: 16,\n paddingVertical: 12,\n gap: 12,\n iconSize: 24,\n closeButtonSize: 24,\n closeIconSize: 20,\n fontSize: 16,\n lineHeight: 20,\n maxWidth: 440,\n};\n\ntype ActionProps = {\n children?: React.ReactNode;\n size?: \"xs\";\n variant?: \"inverse\";\n background?: boolean;\n themeMode?: ToastProps[\"themeMode\"];\n themeProductContext?: ToastProps[\"themeProductContext\"];\n};\n\nexport const Toast: React.FC<ToastProps> = ({\n id,\n type = \"neutral\",\n title,\n description,\n // No clamp by default: the toast grows to fit the description. A hardcoded\n // line limit silently truncated real error copy at narrow widths (XUITSD-20),\n // and translations only make the strings longer. Consumers that genuinely\n // need a cap opt in via `descriptionLines`.\n descriptionLines,\n icon = true,\n action,\n showCloseButton = true,\n progress = false,\n duration,\n onClose,\n colorScheme = DEFAULT_COLOR_SCHEME,\n testID,\n themeMode,\n themeProductContext,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const config = TOAST_CONFIG;\n const [visible, setVisible] = useState(false);\n const [dismissing, setDismissing] = useState(false);\n const [progressComplete, setProgressComplete] = useState(false);\n\n // Hold the latest `onClose` in a ref so `handleClose` can stay referentially\n // stable. `ToastGroup` renders every toast with\n // `onClose={() => onDismiss(toast.id)}` — a brand new closure on each render.\n // Depending on it directly would re-run the auto-dismiss effect (and restart\n // the countdown) every time the toast list changed, so a newly added toast\n // would reset the timers of the ones already on screen. See FEP-861.\n const onCloseRef = useRef(onClose);\n useEffect(() => {\n onCloseRef.current = onClose;\n }, [onClose]);\n\n // Re-entry guard for `handleClose`. A ref rather than the `dismissing` state\n // so the callback does not need `dismissing` in its dependency list.\n const dismissingRef = useRef(false);\n\n useEffect(() => {\n const frame = requestAnimationFrame(() => setVisible(true));\n return () => cancelAnimationFrame(frame);\n }, []);\n\n const handleClose = useCallback(() => {\n if (dismissingRef.current) return;\n dismissingRef.current = true;\n setDismissing(true);\n setTimeout(() => onCloseRef.current?.(), ANIMATION_DURATION);\n }, []);\n\n // Deliberately depends only on `duration` (and the now-stable `handleClose`):\n // each toast owns its countdown and siblings mounting or unmounting must not\n // restart it.\n useEffect(() => {\n if (!duration || duration <= 0) return;\n const timer = setTimeout(handleClose, duration);\n return () => clearTimeout(timer);\n }, [duration, handleClose]);\n\n const shouldAnimateProgress = progress && Boolean(duration && duration > 0);\n\n useEffect(() => {\n if (!shouldAnimateProgress) {\n setProgressComplete(false);\n return;\n }\n\n setProgressComplete(false);\n const frame = requestAnimationFrame(() => setProgressComplete(true));\n\n return () => cancelAnimationFrame(frame);\n }, [duration, shouldAnimateProgress]);\n\n const iconColor = TOAST_COLORS[type];\n const shouldShowIcon = icon !== false;\n const displayIcon =\n icon === true || icon === undefined\n ? getDefaultIcon(type, config.iconSize, iconColor)\n : icon;\n const renderedAction =\n isValidElement<ActionProps>(action) && typeof action.type !== \"string\"\n ? cloneElement(action, {\n size: \"xs\",\n variant: \"inverse\",\n background: false,\n themeMode: \"dark\",\n themeProductContext,\n children: (\n <span style={{ color: colorScheme.actionText }}>\n {action.props.children}\n </span>\n ),\n })\n : action;\n const hasAction = Boolean(action);\n const hasCloseButton = showCloseButton && Boolean(onClose);\n const hasActions = hasAction || hasCloseButton;\n const role = type === \"neutral\" || type === \"success\" ? \"status\" : \"alert\";\n const ariaLive =\n type === \"neutral\" || type === \"success\" ? \"polite\" : \"assertive\";\n\n return (\n <Box\n testID={testID}\n backgroundColor={colorScheme.background}\n borderRadius={theme.shape.toast.borderRadius}\n minHeight={config.minHeight}\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"center\"\n overflow=\"hidden\"\n width={config.maxWidth}\n maxWidth=\"100%\"\n role={role}\n aria-live={ariaLive}\n data-toast-id={id}\n style={{\n opacity: visible && !dismissing ? 1 : 0,\n transform:\n visible && !dismissing ? \"translateY(0)\" : \"translateY(-8px)\",\n transition: `opacity ${ANIMATION_DURATION}ms ease-out, transform ${ANIMATION_DURATION}ms ease-out`,\n boxShadow: colorScheme.shadow,\n backdropFilter: colorScheme.backdropFilter,\n }}\n >\n <Box\n flex={1}\n minHeight={config.minHeight}\n minWidth={0}\n flexDirection=\"row\"\n alignItems=\"center\"\n gap={config.gap}\n paddingHorizontal={config.paddingHorizontal}\n paddingVertical={config.paddingVertical}\n >\n {shouldShowIcon && (\n <Box\n width={config.iconSize}\n height={config.iconSize}\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink={0}\n >\n {icon === true || icon === undefined ? (\n displayIcon\n ) : (\n <Icon size={config.iconSize} color={iconColor}>\n {displayIcon}\n </Icon>\n )}\n </Box>\n )}\n\n <Box flex={1} minWidth={0} gap={2}>\n {title && (\n <Text\n color={colorScheme.text}\n fontSize={config.fontSize}\n lineHeight={config.lineHeight}\n fontWeight=\"500\"\n numberOfLines={1}\n >\n {title}\n </Text>\n )}\n {description && (\n <Text\n testID=\"toast-description\"\n color={colorScheme.descriptionText}\n fontSize={14}\n lineHeight={18}\n numberOfLines={descriptionLines}\n >\n {description}\n </Text>\n )}\n </Box>\n\n {hasActions && (\n <Box\n height={32}\n flexDirection=\"row\"\n alignItems=\"center\"\n gap={config.gap}\n flexShrink={0}\n >\n {renderedAction}\n\n {hasAction && hasCloseButton && (\n <Box\n width={1}\n height=\"100%\"\n backgroundColor={colorScheme.divider}\n testID=\"toast-divider\"\n />\n )}\n\n {hasCloseButton && (\n <Box\n onPress={handleClose}\n width={config.closeButtonSize}\n height={config.closeButtonSize}\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink={0}\n role=\"button\"\n aria-label=\"Dismiss notification\"\n >\n <Remove\n variant=\"line\"\n size={config.closeIconSize}\n color={colorScheme.text}\n />\n </Box>\n )}\n </Box>\n )}\n </Box>\n\n {progress && (\n <Box\n testID=\"toast-progress\"\n position=\"absolute\"\n left={0}\n right={0}\n bottom={0}\n height={PROGRESS_HEIGHT}\n overflow=\"hidden\"\n >\n <Box\n testID=\"toast-progress-bar\"\n width=\"100%\"\n height=\"100%\"\n backgroundColor={iconColor}\n style={{\n transform: `scaleX(${progressComplete ? 0 : 1})`,\n transformOrigin: \"left center\",\n transition: shouldAnimateProgress\n ? `transform ${duration}ms linear`\n : undefined,\n }}\n />\n </Box>\n )}\n </Box>\n );\n};\n\nToast.displayName = \"Toast\";\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n AccessibilityRole,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleToAccessibilityRole: Record<string, AccessibilityRole> = {\n button: \"button\",\n link: \"link\",\n img: \"image\",\n image: \"image\",\n summary: \"summary\",\n switch: \"switch\",\n checkbox: \"checkbox\",\n radio: \"radio\",\n heading: \"header\",\n text: \"text\",\n search: \"search\",\n alert: \"alert\",\n none: \"none\",\n presentation: \"none\",\n progressbar: \"progressbar\",\n scrollbar: \"scrollbar\",\n adjustable: \"adjustable\",\n tab: \"tab\",\n menu: \"menu\",\n menuitem: \"menuitem\",\n list: \"list\",\n timer: \"timer\",\n};\n\n/** RN ViewStyle.gap is typed as number (Yoga gap), not DimensionValue. */\nconst toGapValue = (gap: number | string | undefined): number | undefined => {\n if (gap === undefined) return undefined;\n if (typeof gap === \"number\") return gap;\n const pxMatch = /^(\\d+(?:\\.\\d+)?)px$/.exec(gap);\n if (pxMatch) return Number(pxMatch[1]);\n const asNumber = Number(gap);\n return Number.isFinite(asNumber) ? asNumber : undefined;\n};\n\n/**\n * Yoga has no inline formatting context, so RN's ViewStyle.display only\n * accepts \"flex\" | \"none\". Every inline-ish web value collapses to a plain\n * flex container — the box already fills only its content on native, which is\n * what those callers wanted on web anyway. Returning undefined (rather than\n * \"flex\") leaves RN's own default in place.\n */\nconst toNativeDisplay = (\n display: BoxProps[\"display\"]\n): ViewStyle[\"display\"] => (display === \"none\" ? \"none\" : undefined);\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n display,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n flexShrink,\n gap,\n overflow,\n opacity,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n disabled,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n opacity: opacity ?? (disabled ? 0.5 : undefined),\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n display: toNativeDisplay(display),\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n flexShrink,\n gap: toGapValue(gap),\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Map web a11y props; drop the rest of the web-only attributes before\n // spreading onto RN hosts (unknown props warn at runtime).\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": ariaDisabled,\n \"aria-live\": ariaLive,\n \"aria-busy\": ariaBusy,\n className,\n \"data-testid\": _dataTestId,\n cursor: _cursor,\n type: _type,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n const accessibilityRole =\n (typeof role === \"string\" && roleToAccessibilityRole[role]) ||\n (as === \"button\" ? \"button\" : undefined);\n const isDisabled = Boolean(disabled || ariaDisabled);\n const accessibilityState = {\n disabled: isDisabled || undefined,\n busy: Boolean(ariaBusy) || undefined,\n };\n const accessibilityLiveRegion =\n ariaLive === \"polite\" || ariaLive === \"assertive\" ? ariaLive : undefined;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : alt}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n // Prefer Pressable whenever the node is interactive or explicitly a button,\n // so disabled / aria-disabled controls stay in the a11y tree as buttons.\n if (onPress || as === \"button\") {\n return (\n <Pressable\n onPress={!isDisabled && onPress ? onPress : undefined}\n disabled={isDisabled}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={\n typeof ariaLabel === \"string\" ? ariaLabel : undefined\n }\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : undefined}\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n testID,\n \"data-testid\": dataTestId,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={dataTestId || testID || id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","import React from \"react\";\nimport { View, ViewStyle } from \"react-native\";\nimport { IconProps } from \"@xsolla/xui-primitives-core\";\n\nexport const Icon: React.FC<IconProps> = ({\n children,\n color,\n size,\n testID,\n \"data-testid\": dataTestId,\n}) => {\n const style: ViewStyle = {\n width: typeof size === \"number\" ? size : undefined,\n height: typeof size === \"number\" ? size : undefined,\n alignItems: \"center\",\n justifyContent: \"center\",\n };\n\n // On native, we try to pass the color down to children (like Text primitives)\n // to mimic the CSS inheritance behavior of the web version.\n const childrenWithProps = React.Children.map(children, (child) => {\n if (React.isValidElement(child)) {\n return React.cloneElement(child, {\n color: child.props.color || color,\n // Also pass size if child seems to be an icon that needs it\n size: child.props.size || size,\n });\n }\n return child;\n });\n\n return (\n <View style={style} testID={dataTestId || testID}>\n {childrenWithProps}\n </View>\n );\n};\n","import React, { memo } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme, TOAST_LAYER } from \"@xsolla/xui-core\";\nimport { Toast } from \"./Toast\";\nimport type { ToastGroupProps } from \"./types\";\n\nconst TOAST_GROUP_CONFIG = {\n containerPadding: 12,\n groupGap: 8,\n};\n\nexport const ToastGroup = memo(\n ({\n toasts,\n position = \"top\",\n maxWidth,\n onDismiss,\n testID,\n themeMode,\n themeProductContext,\n }: ToastGroupProps) => {\n useResolvedTheme({ themeMode, themeProductContext });\n const config = TOAST_GROUP_CONFIG;\n\n if (toasts.length === 0) {\n return null;\n }\n\n const positionStyles =\n position === \"top\"\n ? { top: config.containerPadding, bottom: undefined }\n : { top: undefined, bottom: config.containerPadding };\n\n return (\n <Box\n testID={testID}\n position=\"absolute\"\n left={0}\n right={0}\n zIndex={TOAST_LAYER}\n alignItems=\"center\"\n paddingHorizontal={config.containerPadding}\n pointerEvents=\"box-none\"\n {...positionStyles}\n >\n <Box\n width=\"100%\"\n maxWidth={maxWidth}\n gap={config.groupGap}\n flexDirection=\"column\"\n >\n {toasts.map((toast) => (\n <Toast\n key={toast.id}\n id={toast.id}\n type={toast.type}\n title={toast.title}\n description={toast.description}\n descriptionLines={toast.descriptionLines}\n icon={toast.icon}\n action={toast.action}\n showCloseButton={toast.showCloseButton}\n progress={toast.progress}\n duration={toast.duration}\n onClose={() => onDismiss(toast.id)}\n />\n ))}\n </Box>\n </Box>\n );\n }\n);\n\nToastGroup.displayName = \"ToastGroup\";\n","import React, { useCallback, useMemo, useState } from \"react\";\nimport { ToastContext } from \"./ToastContext\";\nimport { ToastGroup } from \"./ToastGroup\";\nimport type { ToastProviderProps, ToastData, ToastOptions } from \"./types\";\n\nlet toastIdCounter = 0;\n\nconst generateToastId = (): string => {\n return `toast-${++toastIdCounter}-${Date.now()}`;\n};\n\nexport const ToastProvider: React.FC<ToastProviderProps> = ({\n children,\n position = \"top\",\n align = \"center\",\n defaultDuration = 5000,\n maxWidth,\n testID,\n}) => {\n const [toasts, setToasts] = useState<ToastData[]>([]);\n\n const dismissToast = useCallback((id: string) => {\n setToasts((prev) => prev.filter((toast) => toast.id !== id));\n }, []);\n\n const dismissAllToasts = useCallback(() => {\n setToasts([]);\n }, []);\n\n const addToast = useCallback(\n (options: ToastOptions): string => {\n const id = generateToastId();\n const duration = options.duration ?? defaultDuration;\n\n const newToast: ToastData = {\n id,\n type: options.type ?? \"neutral\",\n title: options.title,\n description: options.description,\n descriptionLines: options.descriptionLines,\n icon: options.icon,\n action: options.action,\n showCloseButton: options.showCloseButton,\n progress: options.progress,\n duration,\n };\n\n setToasts((prev) => [...prev, newToast]);\n\n return id;\n },\n [defaultDuration]\n );\n\n const contextValue = useMemo(\n () => ({\n toasts,\n addToast,\n dismissToast,\n dismissAllToasts,\n }),\n [toasts, addToast, dismissToast, dismissAllToasts]\n );\n\n return (\n <ToastContext.Provider value={contextValue}>\n {children}\n <ToastGroup\n toasts={toasts}\n position={position}\n align={align}\n maxWidth={maxWidth}\n onDismiss={dismissToast}\n testID={testID}\n />\n </ToastContext.Provider>\n );\n};\n","import { createContext } from \"react\";\nimport type { ToastContextType } from \"./types\";\n\nexport const ToastContext = createContext<ToastContextType | null>(null);\n","import { useContext, useCallback } from \"react\";\nimport { ToastContext } from \"./ToastContext\";\nimport type { ToastOptions } from \"./types\";\n\nexport interface UseToastReturn {\n /** Show a toast with custom options */\n toast: (options: ToastOptions) => string;\n /** Show an alert toast */\n alert: (options: Omit<ToastOptions, \"type\">) => string;\n /** Show a success toast */\n success: (options: Omit<ToastOptions, \"type\">) => string;\n /** Show a neutral toast */\n neutral: (options: Omit<ToastOptions, \"type\">) => string;\n /** Show a warning toast */\n warning: (options: Omit<ToastOptions, \"type\">) => string;\n /** Dismiss a specific toast by ID */\n dismiss: (id: string) => void;\n /** Dismiss all toasts */\n dismissAll: () => void;\n}\n\nexport const useToast = (): UseToastReturn => {\n const context = useContext(ToastContext);\n\n if (!context) {\n throw new Error(\"useToast must be used within a ToastProvider\");\n }\n\n const { addToast, dismissToast, dismissAllToasts } = context;\n\n const toast = useCallback(\n (options: ToastOptions): string => {\n return addToast(options);\n },\n [addToast]\n );\n\n const alert = useCallback(\n (options: Omit<ToastOptions, \"type\">): string => {\n return addToast({ ...options, type: \"alert\" });\n },\n [addToast]\n );\n\n const success = useCallback(\n (options: Omit<ToastOptions, \"type\">): string => {\n return addToast({ ...options, type: \"success\" });\n },\n [addToast]\n );\n\n const neutral = useCallback(\n (options: Omit<ToastOptions, \"type\">): string => {\n return addToast({ ...options, type: \"neutral\" });\n },\n [addToast]\n );\n\n const warning = useCallback(\n (options: Omit<ToastOptions, \"type\">): string => {\n return addToast({ ...options, type: \"warning\" });\n },\n [addToast]\n );\n\n const dismiss = useCallback(\n (id: string): void => {\n dismissToast(id);\n },\n [dismissToast]\n );\n\n const dismissAll = useCallback((): void => {\n dismissAllToasts();\n }, [dismissAllToasts]);\n\n return {\n toast,\n alert,\n success,\n neutral,\n warning,\n dismiss,\n dismissAll,\n };\n};\n"],"mappings":"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","names":["jsx","View","jsx","jsx","useResolvedTheme","jsx","useResolvedTheme","useCallback","useState","jsx","jsxs","useState","useCallback","useCallback","useCallback"]}
1
+ {"version":3,"sources":["../../src/Toast.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../../../foundation/primitives-native/src/Icon.tsx","../../src/ToastGroup.native.tsx","../../src/ToastProvider.tsx","../../src/ToastContext.tsx","../../src/useToast.ts"],"sourcesContent":["import React, {\n cloneElement,\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text, Icon } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme } from \"@xsolla/xui-core\";\nimport {\n CheckCr,\n ExclamationMarkCr,\n InfoCr,\n Remove,\n Warning as WarningIcon,\n} from \"@xsolla/xui-icons-base\";\nimport type { ToastColorScheme, ToastProps, ToastType } from \"./types\";\n\nconst TOAST_COLORS: Record<ToastType, string> = {\n alert: \"#ff553d\",\n success: \"#47d94a\",\n neutral: \"#7899a1\",\n warning: \"#ffb95c\",\n};\nconst DEFAULT_COLOR_SCHEME: ToastColorScheme = {\n background: \"#34474b\",\n text: \"#ffffff\",\n descriptionText: \"rgba(255, 255, 255, 0.7)\",\n divider: \"rgba(255, 255, 255, 0.16)\",\n actionText: \"#ffffff\",\n};\n\nconst getDefaultIcon = (\n type: ToastType,\n size: number,\n color: string\n): React.ReactNode => {\n switch (type) {\n case \"success\":\n return (\n <CheckCr\n variant=\"solid\"\n size={size}\n color={color}\n data-testid=\"toast-icon-success\"\n />\n );\n case \"neutral\":\n return (\n <InfoCr\n variant=\"solid\"\n size={size}\n color={color}\n data-testid=\"toast-icon-neutral\"\n />\n );\n case \"warning\":\n return (\n <WarningIcon\n variant=\"solid\"\n size={size}\n color={color}\n data-testid=\"toast-icon-warning\"\n />\n );\n case \"alert\":\n default:\n return (\n <ExclamationMarkCr\n variant=\"solid\"\n size={size}\n color={color}\n data-testid=\"toast-icon-alert\"\n />\n );\n }\n};\n\nconst ANIMATION_DURATION = 200;\nconst PROGRESS_HEIGHT = 4;\nconst TOAST_CONFIG = {\n minHeight: 56,\n paddingHorizontal: 16,\n paddingVertical: 12,\n gap: 12,\n iconSize: 24,\n closeButtonSize: 24,\n closeIconSize: 20,\n fontSize: 16,\n lineHeight: 20,\n maxWidth: 440,\n};\n\ntype ActionProps = {\n children?: React.ReactNode;\n size?: \"xs\";\n variant?: \"inverse\";\n background?: boolean;\n themeMode?: ToastProps[\"themeMode\"];\n themeProductContext?: ToastProps[\"themeProductContext\"];\n};\n\nexport const Toast: React.FC<ToastProps> = ({\n id,\n type = \"neutral\",\n title,\n // No clamp by default: a long title wraps onto several lines and the toast\n // grows to fit it, per the Figma \"Content behaviour\" spec. A hardcoded\n // single-line clamp cut single-sentence messages off with an ellipsis\n // (XUITSD-118). Consumers that genuinely need a cap opt in via `titleLines`.\n titleLines,\n description,\n // No clamp by default: the toast grows to fit the description. A hardcoded\n // line limit silently truncated real error copy at narrow widths (XUITSD-20),\n // and translations only make the strings longer. Consumers that genuinely\n // need a cap opt in via `descriptionLines`.\n descriptionLines,\n icon = true,\n action,\n showCloseButton = true,\n progress = false,\n duration,\n onClose,\n colorScheme = DEFAULT_COLOR_SCHEME,\n testID,\n themeMode,\n themeProductContext,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const config = TOAST_CONFIG;\n const [visible, setVisible] = useState(false);\n const [dismissing, setDismissing] = useState(false);\n const [progressComplete, setProgressComplete] = useState(false);\n\n // Hold the latest `onClose` in a ref so `handleClose` can stay referentially\n // stable. `ToastGroup` renders every toast with\n // `onClose={() => onDismiss(toast.id)}` — a brand new closure on each render.\n // Depending on it directly would re-run the auto-dismiss effect (and restart\n // the countdown) every time the toast list changed, so a newly added toast\n // would reset the timers of the ones already on screen. See FEP-861.\n const onCloseRef = useRef(onClose);\n useEffect(() => {\n onCloseRef.current = onClose;\n }, [onClose]);\n\n // Re-entry guard for `handleClose`. A ref rather than the `dismissing` state\n // so the callback does not need `dismissing` in its dependency list.\n const dismissingRef = useRef(false);\n\n useEffect(() => {\n const frame = requestAnimationFrame(() => setVisible(true));\n return () => cancelAnimationFrame(frame);\n }, []);\n\n const handleClose = useCallback(() => {\n if (dismissingRef.current) return;\n dismissingRef.current = true;\n setDismissing(true);\n setTimeout(() => onCloseRef.current?.(), ANIMATION_DURATION);\n }, []);\n\n // Deliberately depends only on `duration` (and the now-stable `handleClose`):\n // each toast owns its countdown and siblings mounting or unmounting must not\n // restart it.\n useEffect(() => {\n if (!duration || duration <= 0) return;\n const timer = setTimeout(handleClose, duration);\n return () => clearTimeout(timer);\n }, [duration, handleClose]);\n\n const shouldAnimateProgress = progress && Boolean(duration && duration > 0);\n\n useEffect(() => {\n if (!shouldAnimateProgress) {\n setProgressComplete(false);\n return;\n }\n\n setProgressComplete(false);\n const frame = requestAnimationFrame(() => setProgressComplete(true));\n\n return () => cancelAnimationFrame(frame);\n }, [duration, shouldAnimateProgress]);\n\n const iconColor = TOAST_COLORS[type];\n const shouldShowIcon = icon !== false;\n const displayIcon =\n icon === true || icon === undefined\n ? getDefaultIcon(type, config.iconSize, iconColor)\n : icon;\n const renderedAction =\n isValidElement<ActionProps>(action) && typeof action.type !== \"string\"\n ? cloneElement(action, {\n size: \"xs\",\n variant: \"inverse\",\n background: false,\n themeMode: \"dark\",\n themeProductContext,\n children: (\n <span style={{ color: colorScheme.actionText }}>\n {action.props.children}\n </span>\n ),\n })\n : action;\n const hasAction = Boolean(action);\n const hasCloseButton = showCloseButton && Boolean(onClose);\n const hasActions = hasAction || hasCloseButton;\n const role = type === \"neutral\" || type === \"success\" ? \"status\" : \"alert\";\n const ariaLive =\n type === \"neutral\" || type === \"success\" ? \"polite\" : \"assertive\";\n\n return (\n <Box\n testID={testID}\n backgroundColor={colorScheme.background}\n borderRadius={theme.shape.toast.borderRadius}\n minHeight={config.minHeight}\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"center\"\n overflow=\"hidden\"\n width={config.maxWidth}\n maxWidth=\"100%\"\n role={role}\n aria-live={ariaLive}\n data-toast-id={id}\n style={{\n opacity: visible && !dismissing ? 1 : 0,\n transform:\n visible && !dismissing ? \"translateY(0)\" : \"translateY(-8px)\",\n transition: `opacity ${ANIMATION_DURATION}ms ease-out, transform ${ANIMATION_DURATION}ms ease-out`,\n boxShadow: colorScheme.shadow,\n backdropFilter: colorScheme.backdropFilter,\n }}\n >\n <Box\n flex={1}\n minHeight={config.minHeight}\n minWidth={0}\n flexDirection=\"row\"\n alignItems=\"center\"\n gap={config.gap}\n paddingHorizontal={config.paddingHorizontal}\n paddingVertical={config.paddingVertical}\n >\n {shouldShowIcon && (\n <Box\n width={config.iconSize}\n height={config.iconSize}\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink={0}\n >\n {icon === true || icon === undefined ? (\n displayIcon\n ) : (\n <Icon size={config.iconSize} color={iconColor}>\n {displayIcon}\n </Icon>\n )}\n </Box>\n )}\n\n <Box flex={1} minWidth={0} gap={2}>\n {title && (\n <Text\n testID=\"toast-title\"\n color={colorScheme.text}\n fontSize={config.fontSize}\n lineHeight={config.lineHeight}\n fontWeight=\"500\"\n numberOfLines={titleLines}\n >\n {title}\n </Text>\n )}\n {description && (\n <Text\n testID=\"toast-description\"\n color={colorScheme.descriptionText}\n fontSize={14}\n lineHeight={18}\n numberOfLines={descriptionLines}\n >\n {description}\n </Text>\n )}\n </Box>\n\n {hasActions && (\n <Box\n height={32}\n flexDirection=\"row\"\n alignItems=\"center\"\n gap={config.gap}\n flexShrink={0}\n >\n {renderedAction}\n\n {hasAction && hasCloseButton && (\n <Box\n width={1}\n height=\"100%\"\n backgroundColor={colorScheme.divider}\n testID=\"toast-divider\"\n />\n )}\n\n {hasCloseButton && (\n <Box\n onPress={handleClose}\n width={config.closeButtonSize}\n height={config.closeButtonSize}\n alignItems=\"center\"\n justifyContent=\"center\"\n flexShrink={0}\n role=\"button\"\n aria-label=\"Dismiss notification\"\n >\n <Remove\n variant=\"line\"\n size={config.closeIconSize}\n color={colorScheme.text}\n />\n </Box>\n )}\n </Box>\n )}\n </Box>\n\n {progress && (\n <Box\n testID=\"toast-progress\"\n position=\"absolute\"\n left={0}\n right={0}\n bottom={0}\n height={PROGRESS_HEIGHT}\n overflow=\"hidden\"\n >\n <Box\n testID=\"toast-progress-bar\"\n width=\"100%\"\n height=\"100%\"\n backgroundColor={iconColor}\n style={{\n transform: `scaleX(${progressComplete ? 0 : 1})`,\n transformOrigin: \"left center\",\n transition: shouldAnimateProgress\n ? `transform ${duration}ms linear`\n : undefined,\n }}\n />\n </Box>\n )}\n </Box>\n );\n};\n\nToast.displayName = \"Toast\";\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n AccessibilityRole,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleToAccessibilityRole: Record<string, AccessibilityRole> = {\n button: \"button\",\n link: \"link\",\n img: \"image\",\n image: \"image\",\n summary: \"summary\",\n switch: \"switch\",\n checkbox: \"checkbox\",\n radio: \"radio\",\n heading: \"header\",\n text: \"text\",\n search: \"search\",\n alert: \"alert\",\n none: \"none\",\n presentation: \"none\",\n progressbar: \"progressbar\",\n scrollbar: \"scrollbar\",\n adjustable: \"adjustable\",\n tab: \"tab\",\n menu: \"menu\",\n menuitem: \"menuitem\",\n list: \"list\",\n timer: \"timer\",\n};\n\n/** RN ViewStyle.gap is typed as number (Yoga gap), not DimensionValue. */\nconst toGapValue = (gap: number | string | undefined): number | undefined => {\n if (gap === undefined) return undefined;\n if (typeof gap === \"number\") return gap;\n const pxMatch = /^(\\d+(?:\\.\\d+)?)px$/.exec(gap);\n if (pxMatch) return Number(pxMatch[1]);\n const asNumber = Number(gap);\n return Number.isFinite(asNumber) ? asNumber : undefined;\n};\n\n/**\n * Yoga has no inline formatting context, so RN's ViewStyle.display only\n * accepts \"flex\" | \"none\". Every inline-ish web value collapses to a plain\n * flex container — the box already fills only its content on native, which is\n * what those callers wanted on web anyway. Returning undefined (rather than\n * \"flex\") leaves RN's own default in place.\n */\nconst toNativeDisplay = (\n display: BoxProps[\"display\"]\n): ViewStyle[\"display\"] => (display === \"none\" ? \"none\" : undefined);\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n display,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n flexShrink,\n gap,\n overflow,\n opacity,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n disabled,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n opacity: opacity ?? (disabled ? 0.5 : undefined),\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n display: toNativeDisplay(display),\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n flexShrink,\n gap: toGapValue(gap),\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Map web a11y props; drop the rest of the web-only attributes before\n // spreading onto RN hosts (unknown props warn at runtime).\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": ariaDisabled,\n \"aria-live\": ariaLive,\n \"aria-busy\": ariaBusy,\n className,\n \"data-testid\": _dataTestId,\n cursor: _cursor,\n type: _type,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n const accessibilityRole =\n (typeof role === \"string\" && roleToAccessibilityRole[role]) ||\n (as === \"button\" ? \"button\" : undefined);\n const isDisabled = Boolean(disabled || ariaDisabled);\n const accessibilityState = {\n disabled: isDisabled || undefined,\n busy: Boolean(ariaBusy) || undefined,\n };\n const accessibilityLiveRegion =\n ariaLive === \"polite\" || ariaLive === \"assertive\" ? ariaLive : undefined;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : alt}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n // Prefer Pressable whenever the node is interactive or explicitly a button,\n // so disabled / aria-disabled controls stay in the a11y tree as buttons.\n if (onPress || as === \"button\") {\n return (\n <Pressable\n onPress={!isDisabled && onPress ? onPress : undefined}\n disabled={isDisabled}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={\n typeof ariaLabel === \"string\" ? ariaLabel : undefined\n }\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : undefined}\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n testID,\n \"data-testid\": dataTestId,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={dataTestId || testID || id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","import React from \"react\";\nimport { View, ViewStyle } from \"react-native\";\nimport { IconProps } from \"@xsolla/xui-primitives-core\";\n\nexport const Icon: React.FC<IconProps> = ({\n children,\n color,\n size,\n testID,\n \"data-testid\": dataTestId,\n}) => {\n const style: ViewStyle = {\n width: typeof size === \"number\" ? size : undefined,\n height: typeof size === \"number\" ? size : undefined,\n alignItems: \"center\",\n justifyContent: \"center\",\n };\n\n // On native, we try to pass the color down to children (like Text primitives)\n // to mimic the CSS inheritance behavior of the web version.\n const childrenWithProps = React.Children.map(children, (child) => {\n if (React.isValidElement(child)) {\n return React.cloneElement(child, {\n color: child.props.color || color,\n // Also pass size if child seems to be an icon that needs it\n size: child.props.size || size,\n });\n }\n return child;\n });\n\n return (\n <View style={style} testID={dataTestId || testID}>\n {childrenWithProps}\n </View>\n );\n};\n","import React, { memo } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme, TOAST_LAYER } from \"@xsolla/xui-core\";\nimport { Toast } from \"./Toast\";\nimport type { ToastGroupProps } from \"./types\";\n\nconst TOAST_GROUP_CONFIG = {\n containerPadding: 12,\n groupGap: 8,\n};\n\nexport const ToastGroup = memo(\n ({\n toasts,\n position = \"top\",\n maxWidth,\n onDismiss,\n testID,\n themeMode,\n themeProductContext,\n }: ToastGroupProps) => {\n useResolvedTheme({ themeMode, themeProductContext });\n const config = TOAST_GROUP_CONFIG;\n\n if (toasts.length === 0) {\n return null;\n }\n\n const positionStyles =\n position === \"top\"\n ? { top: config.containerPadding, bottom: undefined }\n : { top: undefined, bottom: config.containerPadding };\n\n return (\n <Box\n testID={testID}\n position=\"absolute\"\n left={0}\n right={0}\n zIndex={TOAST_LAYER}\n alignItems=\"center\"\n paddingHorizontal={config.containerPadding}\n pointerEvents=\"box-none\"\n {...positionStyles}\n >\n <Box\n width=\"100%\"\n maxWidth={maxWidth}\n gap={config.groupGap}\n flexDirection=\"column\"\n >\n {toasts.map((toast) => (\n <Toast\n key={toast.id}\n id={toast.id}\n type={toast.type}\n title={toast.title}\n titleLines={toast.titleLines}\n description={toast.description}\n descriptionLines={toast.descriptionLines}\n icon={toast.icon}\n action={toast.action}\n showCloseButton={toast.showCloseButton}\n progress={toast.progress}\n duration={toast.duration}\n onClose={() => onDismiss(toast.id)}\n />\n ))}\n </Box>\n </Box>\n );\n }\n);\n\nToastGroup.displayName = \"ToastGroup\";\n","import React, { useCallback, useMemo, useState } from \"react\";\nimport { ToastContext } from \"./ToastContext\";\nimport { ToastGroup } from \"./ToastGroup\";\nimport type { ToastProviderProps, ToastData, ToastOptions } from \"./types\";\n\nlet toastIdCounter = 0;\n\nconst generateToastId = (): string => {\n return `toast-${++toastIdCounter}-${Date.now()}`;\n};\n\nexport const ToastProvider: React.FC<ToastProviderProps> = ({\n children,\n position = \"top\",\n align = \"center\",\n defaultDuration = 5000,\n maxWidth,\n testID,\n}) => {\n const [toasts, setToasts] = useState<ToastData[]>([]);\n\n const dismissToast = useCallback((id: string) => {\n setToasts((prev) => prev.filter((toast) => toast.id !== id));\n }, []);\n\n const dismissAllToasts = useCallback(() => {\n setToasts([]);\n }, []);\n\n const addToast = useCallback(\n (options: ToastOptions): string => {\n const id = generateToastId();\n const duration = options.duration ?? defaultDuration;\n\n const newToast: ToastData = {\n id,\n type: options.type ?? \"neutral\",\n title: options.title,\n titleLines: options.titleLines,\n description: options.description,\n descriptionLines: options.descriptionLines,\n icon: options.icon,\n action: options.action,\n showCloseButton: options.showCloseButton,\n progress: options.progress,\n duration,\n };\n\n setToasts((prev) => [...prev, newToast]);\n\n return id;\n },\n [defaultDuration]\n );\n\n const contextValue = useMemo(\n () => ({\n toasts,\n addToast,\n dismissToast,\n dismissAllToasts,\n }),\n [toasts, addToast, dismissToast, dismissAllToasts]\n );\n\n return (\n <ToastContext.Provider value={contextValue}>\n {children}\n <ToastGroup\n toasts={toasts}\n position={position}\n align={align}\n maxWidth={maxWidth}\n onDismiss={dismissToast}\n testID={testID}\n />\n </ToastContext.Provider>\n );\n};\n","import { createContext } from \"react\";\nimport type { ToastContextType } from \"./types\";\n\nexport const ToastContext = createContext<ToastContextType | null>(null);\n","import { useContext, useCallback } from \"react\";\nimport { ToastContext } from \"./ToastContext\";\nimport type { ToastOptions } from \"./types\";\n\nexport interface UseToastReturn {\n /** Show a toast with custom options */\n toast: (options: ToastOptions) => string;\n /** Show an alert toast */\n alert: (options: Omit<ToastOptions, \"type\">) => string;\n /** Show a success toast */\n success: (options: Omit<ToastOptions, \"type\">) => string;\n /** Show a neutral toast */\n neutral: (options: Omit<ToastOptions, \"type\">) => string;\n /** Show a warning toast */\n warning: (options: Omit<ToastOptions, \"type\">) => string;\n /** Dismiss a specific toast by ID */\n dismiss: (id: string) => void;\n /** Dismiss all toasts */\n dismissAll: () => void;\n}\n\nexport const useToast = (): UseToastReturn => {\n const context = useContext(ToastContext);\n\n if (!context) {\n throw new Error(\"useToast must be used within a ToastProvider\");\n }\n\n const { addToast, dismissToast, dismissAllToasts } = context;\n\n const toast = useCallback(\n (options: ToastOptions): string => {\n return addToast(options);\n },\n [addToast]\n );\n\n const alert = useCallback(\n (options: Omit<ToastOptions, \"type\">): string => {\n return addToast({ ...options, type: \"alert\" });\n },\n [addToast]\n );\n\n const success = useCallback(\n (options: Omit<ToastOptions, \"type\">): string => {\n return addToast({ ...options, type: \"success\" });\n },\n [addToast]\n );\n\n const neutral = useCallback(\n (options: Omit<ToastOptions, \"type\">): string => {\n return addToast({ ...options, type: \"neutral\" });\n },\n [addToast]\n );\n\n const warning = useCallback(\n (options: Omit<ToastOptions, \"type\">): string => {\n return addToast({ ...options, type: \"warning\" });\n },\n [addToast]\n );\n\n const dismiss = useCallback(\n (id: string): void => {\n dismissToast(id);\n },\n [dismissToast]\n );\n\n const dismissAll = useCallback((): void => {\n dismissAllToasts();\n }, [dismissAllToasts]);\n\n return {\n toast,\n alert,\n success,\n neutral,\n warning,\n dismiss,\n dismissAll,\n };\n};\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACNP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AAqND;AAlNN,IAAM,0BAA6D;AAAA,EACjE,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,KAAK;AAAA,EACL,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,cAAc;AAAA,EACd,aAAa;AAAA,EACb,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,MAAM;AAAA,EACN,UAAU;AAAA,EACV,MAAM;AAAA,EACN,OAAO;AACT;AAGA,IAAM,aAAa,CAAC,QAAyD;AAC3E,MAAI,QAAQ,OAAW,QAAO;AAC9B,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,UAAU,sBAAsB,KAAK,GAAG;AAC9C,MAAI,QAAS,QAAO,OAAO,QAAQ,CAAC,CAAC;AACrC,QAAM,WAAW,OAAO,GAAG;AAC3B,SAAO,OAAO,SAAS,QAAQ,IAAI,WAAW;AAChD;AASA,IAAM,kBAAkB,CACtB,YAC0B,YAAY,SAAS,SAAS;AAEnD,IAAM,MAA0B,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAM;AACJ,QAAM,oBAAoB,CAAC,aAAkC;AAAA,IAC3D,iBACE,WAAW,YAAY,kBACnB,WAAW,kBACX;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,YAAY,WAAW,MAAM;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,gBAAgB,OAAO;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,WAAW,GAAG;AAAA,IACnB,GAAI;AAAA,EACN;AAEA,QAAM,cAAc,cAAc;AAKlC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,aAAa;AAAA,IACb,aAAa;AAAA,IACb;AAAA,IACA,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,oBACH,OAAO,SAAS,YAAY,wBAAwB,IAAI,MACxD,OAAO,WAAW,WAAW;AAChC,QAAM,aAAa,QAAQ,YAAY,YAAY;AACnD,QAAM,qBAAqB;AAAA,IACzB,UAAU,cAAc;AAAA,IACxB,MAAM,QAAQ,QAAQ,KAAK;AAAA,EAC7B;AACA,QAAM,0BACJ,aAAa,YAAY,aAAa,cAAc,WAAW;AAGjE,MAAI,OAAO,SAAS,KAAK;AACvB,UAAM,aAAyB;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI;AAAA,IACN;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,EAAE,KAAK,IAAI;AAAA,QACnB,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,oBAAoB,OAAO,cAAc,WAAW,YAAY;AAAA,QAChE,YAAW;AAAA,QACV,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AAIA,MAAI,WAAW,OAAO,UAAU;AAC9B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,CAAC,cAAc,UAAU,UAAU;AAAA,QAC5C,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,CAAC,EAAE,QAAQ,MAAM,kBAAkB,OAAO;AAAA,QACjD,QAAQ;AAAA,QACR;AAAA,QACA,oBACE,OAAO,cAAc,WAAW,YAAY;AAAA,QAE9C;AAAA,QACA;AAAA,QACC,GAAG;AAAA,QAEH;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,kBAAkB;AAAA,MACzB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,oBAAoB,OAAO,cAAc,WAAW,YAAY;AAAA,MAChE;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;;;ACxRA;AAAA,EACE,QAAQ;AAAA,EAGR;AAAA,OACK;AAqEH,gBAAAA,YAAA;AAlEJ,IAAM,UAA6C;AAAA,EACjD,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AACR;AAEA,IAAM,oBAAoB,CACxB,UACuB;AACvB,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,SAAS,WAAW,KAAK;AAC/B,SAAO,MAAM,MAAM,IAAI,SAAY;AACrC;AAEO,IAAM,OAA4B,CAAC;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,OAAO;AAAA,EACP,GAAG;AACL,MAAM;AACJ,MAAI,qBAAqB,aACrB,WAAW,MAAM,GAAG,EAAE,CAAC,EAAE,QAAQ,SAAS,EAAE,EAAE,KAAK,IACnD;AAEJ,MACE,uBAAuB,gBACvB,uBAAuB,qBACvB,uBAAuB,iBACvB;AACA,yBAAqB;AAAA,EACvB;AAEA,QAAM,gBAAgB,WAAW,QAAQ,SAAS;AAElD,QAAM,YAAuB;AAAA,IAC3B,OAAO,SAAS,eAAe;AAAA,IAC/B,UAAU,OAAO,aAAa,WAAW,WAAW;AAAA,IACpD;AAAA,IACA,YAAY;AAAA,IACZ,oBAAoB,MAAM;AAAA,IAC1B,WAAW,aAAa,eAAe;AAAA,IACvC,YAAY,kBAAkB,cAAc,eAAe,UAAU;AAAA,IACrE,WAAW;AAAA,MACT,eAAe;AAAA,IACjB;AAAA,IACA,cAAc;AAAA,MACZ,eAAe;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,oBAAoB,OAAO,QAAQ,IAAI,IAAI;AAEjD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,MACP;AAAA,MACA,QAAQ,cAAc,UAAU;AAAA,MAChC;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;;;ACpFA,OAAO,WAAW;AAClB,SAAS,QAAAC,aAAuB;AA+B5B,gBAAAC,YAAA;AA5BG,IAAM,OAA4B,CAAC;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,MAAM;AACJ,QAAM,QAAmB;AAAA,IACvB,OAAO,OAAO,SAAS,WAAW,OAAO;AAAA,IACzC,QAAQ,OAAO,SAAS,WAAW,OAAO;AAAA,IAC1C,YAAY;AAAA,IACZ,gBAAgB;AAAA,EAClB;AAIA,QAAM,oBAAoB,MAAM,SAAS,IAAI,UAAU,CAAC,UAAU;AAChE,QAAI,MAAM,eAAe,KAAK,GAAG;AAC/B,aAAO,MAAM,aAAa,OAAO;AAAA,QAC/B,OAAO,MAAM,MAAM,SAAS;AAAA;AAAA,QAE5B,MAAM,MAAM,MAAM,QAAQ;AAAA,MAC5B,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT,CAAC;AAED,SACE,gBAAAA,KAACD,OAAA,EAAK,OAAc,QAAQ,cAAc,QACvC,6BACH;AAEJ;;;AH1BA,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,OACN;AAyBC,gBAAAE,MAgOA,YAhOA;AAtBR,IAAM,eAA0C;AAAA,EAC9C,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AACX;AACA,IAAM,uBAAyC;AAAA,EAC7C,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,YAAY;AACd;AAEA,IAAM,iBAAiB,CACrB,MACA,MACA,UACoB;AACpB,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR;AAAA,UACA;AAAA,UACA,eAAY;AAAA;AAAA,MACd;AAAA,IAEJ,KAAK;AACH,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR;AAAA,UACA;AAAA,UACA,eAAY;AAAA;AAAA,MACd;AAAA,IAEJ,KAAK;AACH,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR;AAAA,UACA;AAAA,UACA,eAAY;AAAA;AAAA,MACd;AAAA,IAEJ,KAAK;AAAA,IACL;AACE,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR;AAAA,UACA;AAAA,UACA,eAAY;AAAA;AAAA,MACd;AAAA,EAEN;AACF;AAEA,IAAM,qBAAqB;AAC3B,IAAM,kBAAkB;AACxB,IAAM,eAAe;AAAA,EACnB,WAAW;AAAA,EACX,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,KAAK;AAAA,EACL,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,UAAU;AACZ;AAWO,IAAM,QAA8B,CAAC;AAAA,EAC1C;AAAA,EACA,OAAO;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,kBAAkB;AAAA,EAClB,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,EAAE,MAAM,IAAI,iBAAiB,EAAE,WAAW,oBAAoB,CAAC;AACrE,QAAM,SAAS;AACf,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAS,KAAK;AAQ9D,QAAM,aAAa,OAAO,OAAO;AACjC,YAAU,MAAM;AACd,eAAW,UAAU;AAAA,EACvB,GAAG,CAAC,OAAO,CAAC;AAIZ,QAAM,gBAAgB,OAAO,KAAK;AAElC,YAAU,MAAM;AACd,UAAM,QAAQ,sBAAsB,MAAM,WAAW,IAAI,CAAC;AAC1D,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACzC,GAAG,CAAC,CAAC;AAEL,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI,cAAc,QAAS;AAC3B,kBAAc,UAAU;AACxB,kBAAc,IAAI;AAClB,eAAW,MAAM,WAAW,UAAU,GAAG,kBAAkB;AAAA,EAC7D,GAAG,CAAC,CAAC;AAKL,YAAU,MAAM;AACd,QAAI,CAAC,YAAY,YAAY,EAAG;AAChC,UAAM,QAAQ,WAAW,aAAa,QAAQ;AAC9C,WAAO,MAAM,aAAa,KAAK;AAAA,EACjC,GAAG,CAAC,UAAU,WAAW,CAAC;AAE1B,QAAM,wBAAwB,YAAY,QAAQ,YAAY,WAAW,CAAC;AAE1E,YAAU,MAAM;AACd,QAAI,CAAC,uBAAuB;AAC1B,0BAAoB,KAAK;AACzB;AAAA,IACF;AAEA,wBAAoB,KAAK;AACzB,UAAM,QAAQ,sBAAsB,MAAM,oBAAoB,IAAI,CAAC;AAEnE,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACzC,GAAG,CAAC,UAAU,qBAAqB,CAAC;AAEpC,QAAM,YAAY,aAAa,IAAI;AACnC,QAAM,iBAAiB,SAAS;AAChC,QAAM,cACJ,SAAS,QAAQ,SAAS,SACtB,eAAe,MAAM,OAAO,UAAU,SAAS,IAC/C;AACN,QAAM,iBACJ,eAA4B,MAAM,KAAK,OAAO,OAAO,SAAS,WAC1D,aAAa,QAAQ;AAAA,IACnB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,WAAW;AAAA,IACX;AAAA,IACA,UACE,gBAAAA,KAAC,UAAK,OAAO,EAAE,OAAO,YAAY,WAAW,GAC1C,iBAAO,MAAM,UAChB;AAAA,EAEJ,CAAC,IACD;AACN,QAAM,YAAY,QAAQ,MAAM;AAChC,QAAM,iBAAiB,mBAAmB,QAAQ,OAAO;AACzD,QAAM,aAAa,aAAa;AAChC,QAAM,OAAO,SAAS,aAAa,SAAS,YAAY,WAAW;AACnE,QAAM,WACJ,SAAS,aAAa,SAAS,YAAY,WAAW;AAExD,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,iBAAiB,YAAY;AAAA,MAC7B,cAAc,MAAM,MAAM,MAAM;AAAA,MAChC,WAAW,OAAO;AAAA,MAClB,eAAc;AAAA,MACd,YAAW;AAAA,MACX,gBAAe;AAAA,MACf,UAAS;AAAA,MACT,OAAO,OAAO;AAAA,MACd,UAAS;AAAA,MACT;AAAA,MACA,aAAW;AAAA,MACX,iBAAe;AAAA,MACf,OAAO;AAAA,QACL,SAAS,WAAW,CAAC,aAAa,IAAI;AAAA,QACtC,WACE,WAAW,CAAC,aAAa,kBAAkB;AAAA,QAC7C,YAAY,WAAW,kBAAkB,0BAA0B,kBAAkB;AAAA,QACrF,WAAW,YAAY;AAAA,QACvB,gBAAgB,YAAY;AAAA,MAC9B;AAAA,MAEA;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,WAAW,OAAO;AAAA,YAClB,UAAU;AAAA,YACV,eAAc;AAAA,YACd,YAAW;AAAA,YACX,KAAK,OAAO;AAAA,YACZ,mBAAmB,OAAO;AAAA,YAC1B,iBAAiB,OAAO;AAAA,YAEvB;AAAA,gCACC,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO,OAAO;AAAA,kBACd,QAAQ,OAAO;AAAA,kBACf,YAAW;AAAA,kBACX,gBAAe;AAAA,kBACf,YAAY;AAAA,kBAEX,mBAAS,QAAQ,SAAS,SACzB,cAEA,gBAAAA,KAAC,QAAK,MAAM,OAAO,UAAU,OAAO,WACjC,uBACH;AAAA;AAAA,cAEJ;AAAA,cAGF,qBAAC,OAAI,MAAM,GAAG,UAAU,GAAG,KAAK,GAC7B;AAAA,yBACC,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,QAAO;AAAA,oBACP,OAAO,YAAY;AAAA,oBACnB,UAAU,OAAO;AAAA,oBACjB,YAAY,OAAO;AAAA,oBACnB,YAAW;AAAA,oBACX,eAAe;AAAA,oBAEd;AAAA;AAAA,gBACH;AAAA,gBAED,eACC,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,QAAO;AAAA,oBACP,OAAO,YAAY;AAAA,oBACnB,UAAU;AAAA,oBACV,YAAY;AAAA,oBACZ,eAAe;AAAA,oBAEd;AAAA;AAAA,gBACH;AAAA,iBAEJ;AAAA,cAEC,cACC;AAAA,gBAAC;AAAA;AAAA,kBACC,QAAQ;AAAA,kBACR,eAAc;AAAA,kBACd,YAAW;AAAA,kBACX,KAAK,OAAO;AAAA,kBACZ,YAAY;AAAA,kBAEX;AAAA;AAAA,oBAEA,aAAa,kBACZ,gBAAAA;AAAA,sBAAC;AAAA;AAAA,wBACC,OAAO;AAAA,wBACP,QAAO;AAAA,wBACP,iBAAiB,YAAY;AAAA,wBAC7B,QAAO;AAAA;AAAA,oBACT;AAAA,oBAGD,kBACC,gBAAAA;AAAA,sBAAC;AAAA;AAAA,wBACC,SAAS;AAAA,wBACT,OAAO,OAAO;AAAA,wBACd,QAAQ,OAAO;AAAA,wBACf,YAAW;AAAA,wBACX,gBAAe;AAAA,wBACf,YAAY;AAAA,wBACZ,MAAK;AAAA,wBACL,cAAW;AAAA,wBAEX,0BAAAA;AAAA,0BAAC;AAAA;AAAA,4BACC,SAAQ;AAAA,4BACR,MAAM,OAAO;AAAA,4BACb,OAAO,YAAY;AAAA;AAAA,wBACrB;AAAA;AAAA,oBACF;AAAA;AAAA;AAAA,cAEJ;AAAA;AAAA;AAAA,QAEJ;AAAA,QAEC,YACC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,QAAO;AAAA,YACP,UAAS;AAAA,YACT,MAAM;AAAA,YACN,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,UAAS;AAAA,YAET,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,QAAO;AAAA,gBACP,OAAM;AAAA,gBACN,QAAO;AAAA,gBACP,iBAAiB;AAAA,gBACjB,OAAO;AAAA,kBACL,WAAW,UAAU,mBAAmB,IAAI,CAAC;AAAA,kBAC7C,iBAAiB;AAAA,kBACjB,YAAY,wBACR,aAAa,QAAQ,cACrB;AAAA,gBACN;AAAA;AAAA,YACF;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,MAAM,cAAc;;;AI1WpB,SAAgB,YAAY;AAG5B,SAAS,oBAAAC,mBAAkB,mBAAmB;AAkDlC,gBAAAC,YAAA;AA9CZ,IAAM,qBAAqB;AAAA,EACzB,kBAAkB;AAAA,EAClB,UAAU;AACZ;AAEO,IAAM,aAAa;AAAA,EACxB,CAAC;AAAA,IACC;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAAuB;AACrB,IAAAC,kBAAiB,EAAE,WAAW,oBAAoB,CAAC;AACnD,UAAM,SAAS;AAEf,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO;AAAA,IACT;AAEA,UAAM,iBACJ,aAAa,QACT,EAAE,KAAK,OAAO,kBAAkB,QAAQ,OAAU,IAClD,EAAE,KAAK,QAAW,QAAQ,OAAO,iBAAiB;AAExD,WACE,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,UAAS;AAAA,QACT,MAAM;AAAA,QACN,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,YAAW;AAAA,QACX,mBAAmB,OAAO;AAAA,QAC1B,eAAc;AAAA,QACb,GAAG;AAAA,QAEJ,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN;AAAA,YACA,KAAK,OAAO;AAAA,YACZ,eAAc;AAAA,YAEb,iBAAO,IAAI,CAAC,UACX,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC,IAAI,MAAM;AAAA,gBACV,MAAM,MAAM;AAAA,gBACZ,OAAO,MAAM;AAAA,gBACb,YAAY,MAAM;AAAA,gBAClB,aAAa,MAAM;AAAA,gBACnB,kBAAkB,MAAM;AAAA,gBACxB,MAAM,MAAM;AAAA,gBACZ,QAAQ,MAAM;AAAA,gBACd,iBAAiB,MAAM;AAAA,gBACvB,UAAU,MAAM;AAAA,gBAChB,UAAU,MAAM;AAAA,gBAChB,SAAS,MAAM,UAAU,MAAM,EAAE;AAAA;AAAA,cAZ5B,MAAM;AAAA,YAab,CACD;AAAA;AAAA,QACH;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AAEA,WAAW,cAAc;;;AC3EzB,SAAgB,eAAAE,cAAa,SAAS,YAAAC,iBAAgB;;;ACAtD,SAAS,qBAAqB;AAGvB,IAAM,eAAe,cAAuC,IAAI;;;AD+DnE,SAEE,OAAAC,MAFF,QAAAC,aAAA;AA7DJ,IAAI,iBAAiB;AAErB,IAAM,kBAAkB,MAAc;AACpC,SAAO,SAAS,EAAE,cAAc,IAAI,KAAK,IAAI,CAAC;AAChD;AAEO,IAAM,gBAA8C,CAAC;AAAA,EAC1D;AAAA,EACA,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,kBAAkB;AAAA,EAClB;AAAA,EACA;AACF,MAAM;AACJ,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAsB,CAAC,CAAC;AAEpD,QAAM,eAAeC,aAAY,CAAC,OAAe;AAC/C,cAAU,CAAC,SAAS,KAAK,OAAO,CAAC,UAAU,MAAM,OAAO,EAAE,CAAC;AAAA,EAC7D,GAAG,CAAC,CAAC;AAEL,QAAM,mBAAmBA,aAAY,MAAM;AACzC,cAAU,CAAC,CAAC;AAAA,EACd,GAAG,CAAC,CAAC;AAEL,QAAM,WAAWA;AAAA,IACf,CAAC,YAAkC;AACjC,YAAM,KAAK,gBAAgB;AAC3B,YAAM,WAAW,QAAQ,YAAY;AAErC,YAAM,WAAsB;AAAA,QAC1B;AAAA,QACA,MAAM,QAAQ,QAAQ;AAAA,QACtB,OAAO,QAAQ;AAAA,QACf,YAAY,QAAQ;AAAA,QACpB,aAAa,QAAQ;AAAA,QACrB,kBAAkB,QAAQ;AAAA,QAC1B,MAAM,QAAQ;AAAA,QACd,QAAQ,QAAQ;AAAA,QAChB,iBAAiB,QAAQ;AAAA,QACzB,UAAU,QAAQ;AAAA,QAClB;AAAA,MACF;AAEA,gBAAU,CAAC,SAAS,CAAC,GAAG,MAAM,QAAQ,CAAC;AAEvC,aAAO;AAAA,IACT;AAAA,IACA,CAAC,eAAe;AAAA,EAClB;AAEA,QAAM,eAAe;AAAA,IACnB,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,UAAU,cAAc,gBAAgB;AAAA,EACnD;AAEA,SACE,gBAAAF,MAAC,aAAa,UAAb,EAAsB,OAAO,cAC3B;AAAA;AAAA,IACD,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;;;AE9EA,SAAS,YAAY,eAAAI,oBAAmB;AAqBjC,IAAM,WAAW,MAAsB;AAC5C,QAAM,UAAU,WAAW,YAAY;AAEvC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AAEA,QAAM,EAAE,UAAU,cAAc,iBAAiB,IAAI;AAErD,QAAM,QAAQC;AAAA,IACZ,CAAC,YAAkC;AACjC,aAAO,SAAS,OAAO;AAAA,IACzB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,QAAQA;AAAA,IACZ,CAAC,YAAgD;AAC/C,aAAO,SAAS,EAAE,GAAG,SAAS,MAAM,QAAQ,CAAC;AAAA,IAC/C;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,UAAUA;AAAA,IACd,CAAC,YAAgD;AAC/C,aAAO,SAAS,EAAE,GAAG,SAAS,MAAM,UAAU,CAAC;AAAA,IACjD;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,UAAUA;AAAA,IACd,CAAC,YAAgD;AAC/C,aAAO,SAAS,EAAE,GAAG,SAAS,MAAM,UAAU,CAAC;AAAA,IACjD;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,UAAUA;AAAA,IACd,CAAC,YAAgD;AAC/C,aAAO,SAAS,EAAE,GAAG,SAAS,MAAM,UAAU,CAAC;AAAA,IACjD;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,UAAUA;AAAA,IACd,CAAC,OAAqB;AACpB,mBAAa,EAAE;AAAA,IACjB;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAEA,QAAM,aAAaA,aAAY,MAAY;AACzC,qBAAiB;AAAA,EACnB,GAAG,CAAC,gBAAgB,CAAC;AAErB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":["jsx","View","jsx","jsx","useResolvedTheme","jsx","useResolvedTheme","useCallback","useState","jsx","jsxs","useState","useCallback","useCallback","useCallback"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xsolla/xui-toast",
3
- "version": "0.225.0",
3
+ "version": "0.226.0",
4
4
  "main": "./web/index.js",
5
5
  "module": "./web/index.mjs",
6
6
  "types": "./web/index.d.ts",
@@ -14,10 +14,10 @@
14
14
  "test:coverage": "vitest run --coverage"
15
15
  },
16
16
  "dependencies": {
17
- "@xsolla/xui-core": "0.225.0",
18
- "@xsolla/xui-icons": "0.225.0",
19
- "@xsolla/xui-icons-base": "0.225.0",
20
- "@xsolla/xui-primitives-core": "0.225.0"
17
+ "@xsolla/xui-core": "0.226.0",
18
+ "@xsolla/xui-icons": "0.226.0",
19
+ "@xsolla/xui-icons-base": "0.226.0",
20
+ "@xsolla/xui-primitives-core": "0.226.0"
21
21
  },
22
22
  "peerDependencies": {
23
23
  "react": ">=16.8.0",
package/web/index.d.mts CHANGED
@@ -9,6 +9,7 @@ interface ToastData {
9
9
  id: string;
10
10
  type: ToastType;
11
11
  title?: string;
12
+ titleLines?: number;
12
13
  description?: string;
13
14
  descriptionLines?: number;
14
15
  icon?: boolean | ReactNode;
@@ -30,6 +31,13 @@ interface ToastProps extends ThemeOverrideProps {
30
31
  id: string;
31
32
  type?: ToastType;
32
33
  title?: string;
34
+ /**
35
+ * Clamp the title to this many lines. Omitted (the default) means no clamp:
36
+ * a long title wraps onto several lines and the toast grows to fit it, as
37
+ * specified in the Figma toast "Content behaviour" section. Opt into a limit
38
+ * only when the surrounding layout genuinely cannot absorb the extra height.
39
+ */
40
+ titleLines?: number;
33
41
  description?: string;
34
42
  /**
35
43
  * Clamp the description to this many lines. Omitted (the default) means no
@@ -51,6 +59,8 @@ interface ToastProps extends ThemeOverrideProps {
51
59
  interface ToastOptions {
52
60
  type?: ToastType;
53
61
  title?: string;
62
+ /** Clamp the title to this many lines. Omitted = no clamp. */
63
+ titleLines?: number;
54
64
  description?: string;
55
65
  /** Clamp the description to this many lines. Omitted = no clamp. */
56
66
  descriptionLines?: number;
package/web/index.d.ts CHANGED
@@ -9,6 +9,7 @@ interface ToastData {
9
9
  id: string;
10
10
  type: ToastType;
11
11
  title?: string;
12
+ titleLines?: number;
12
13
  description?: string;
13
14
  descriptionLines?: number;
14
15
  icon?: boolean | ReactNode;
@@ -30,6 +31,13 @@ interface ToastProps extends ThemeOverrideProps {
30
31
  id: string;
31
32
  type?: ToastType;
32
33
  title?: string;
34
+ /**
35
+ * Clamp the title to this many lines. Omitted (the default) means no clamp:
36
+ * a long title wraps onto several lines and the toast grows to fit it, as
37
+ * specified in the Figma toast "Content behaviour" section. Opt into a limit
38
+ * only when the surrounding layout genuinely cannot absorb the extra height.
39
+ */
40
+ titleLines?: number;
33
41
  description?: string;
34
42
  /**
35
43
  * Clamp the description to this many lines. Omitted (the default) means no
@@ -51,6 +59,8 @@ interface ToastProps extends ThemeOverrideProps {
51
59
  interface ToastOptions {
52
60
  type?: ToastType;
53
61
  title?: string;
62
+ /** Clamp the title to this many lines. Omitted = no clamp. */
63
+ titleLines?: number;
54
64
  description?: string;
55
65
  /** Clamp the description to this many lines. Omitted = no clamp. */
56
66
  descriptionLines?: number;
package/web/index.js CHANGED
@@ -486,6 +486,11 @@ var Toast = ({
486
486
  id,
487
487
  type = "neutral",
488
488
  title,
489
+ // No clamp by default: a long title wraps onto several lines and the toast
490
+ // grows to fit it, per the Figma "Content behaviour" spec. A hardcoded
491
+ // single-line clamp cut single-sentence messages off with an ellipsis
492
+ // (XUITSD-118). Consumers that genuinely need a cap opt in via `titleLines`.
493
+ titleLines,
489
494
  description,
490
495
  // No clamp by default: the toast grows to fit the description. A hardcoded
491
496
  // line limit silently truncated real error copy at narrow widths (XUITSD-20),
@@ -605,11 +610,12 @@ var Toast = ({
605
610
  title && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
606
611
  Text,
607
612
  {
613
+ testID: "toast-title",
608
614
  color: colorScheme.text,
609
615
  fontSize: config.fontSize,
610
616
  lineHeight: config.lineHeight,
611
617
  fontWeight: "500",
612
- numberOfLines: 1,
618
+ numberOfLines: titleLines,
613
619
  children: title
614
620
  }
615
621
  ),
@@ -760,6 +766,7 @@ var ToastGroup = (0, import_react4.memo)(
760
766
  id: toast.id,
761
767
  type: toast.type,
762
768
  title: toast.title,
769
+ titleLines: toast.titleLines,
763
770
  description: toast.description,
764
771
  descriptionLines: toast.descriptionLines,
765
772
  icon: toast.icon,
@@ -817,6 +824,7 @@ var ToastProvider = ({
817
824
  id,
818
825
  type: options.type ?? "neutral",
819
826
  title: options.title,
827
+ titleLines: options.titleLines,
820
828
  description: options.description,
821
829
  descriptionLines: options.descriptionLines,
822
830
  icon: options.icon,