import { Ref } from "react"; import { AccessibilityRole, GestureResponderEvent, Pressable, StyleSheet, View, ViewStyle } from "react-native"; import Animated from "react-native-reanimated"; import { useIOTheme, useIOThemeContext } from "../../context"; import { IOBannerBigSpacing, IOBannerRadius } from "../../core"; import { hexToRgba, IOColors } from "../../core/IOColors"; import { useScaleAnimation } from "../../hooks"; import { WithTestID } from "../../utils/types"; import { IconButton } from "../buttons"; import { VSpacer } from "../layout"; import { IOPictogramsBleed, IOPictogramSizeScale, PictogramBleed } from "../pictograms"; import { BodySmall, buttonTextFontSize, H6, IOText } from "../typography"; /* Styles */ const sizePictogram: IOPictogramSizeScale = 80; const closeButtonDistanceFromEdge: number = 10; const closeButtonOpacity = 0.6; const bannerPadding = IOBannerBigSpacing; const styles = StyleSheet.create({ container: { flexDirection: "row", alignItems: "flex-start", alignContent: "center", padding: bannerPadding, borderRadius: IOBannerRadius, borderCurve: "continuous" }, bleedPictogram: { marginRight: -bannerPadding }, closeIconButton: { position: "absolute", right: closeButtonDistanceFromEdge, top: closeButtonDistanceFromEdge, opacity: closeButtonOpacity } }); /* Component Types */ type BaseBannerProps = WithTestID<{ ref?: Ref; color: "neutral" | "turquoise"; pictogramName: IOPictogramsBleed; // A11y related props accessibilityLabel?: string; accessibilityHint?: string; }>; /* Description only */ type BannerPropsDescOnly = { title: never; content?: string }; /* Title only */ type BannerPropsTitleOnly = { title?: string; content: never }; /* Title + Description */ type BannerPropsTitleAndDesc = { title?: string; content?: string }; type RequiredBannerProps = | BannerPropsDescOnly | BannerPropsTitleOnly | BannerPropsTitleAndDesc; type BannerActionProps = | { action: string; onPress: (event: GestureResponderEvent) => void; accessibilityRole?: Extract; } | { action?: never; onPress?: never; accessibilityRole?: AccessibilityRole; }; // Banner will display a close button if this event is provided type BannerCloseProps = | { onClose?: (event: GestureResponderEvent) => void; labelClose?: string; } | { onClose?: never; labelClose?: never; }; export type Banner = BaseBannerProps & RequiredBannerProps & BannerActionProps & BannerCloseProps; // COMPONENT CONFIGURATION /* Used to generate automatically the colour variants in the Design System screen */ export const bannerBackgroundColours: Array = [ "neutral", "turquoise" ]; const mapBackgroundColorLightMode: Record< NonNullable, IOColors > = { neutral: "grey-50", turquoise: "turquoise-50" }; const mapBackgroundColorDarkMode: Record< NonNullable, IOColors > = { neutral: "grey-50", turquoise: "turquoise-300" }; export const Banner = ({ color, pictogramName, title, content, action, labelClose, onPress, onClose, accessibilityHint, accessibilityLabel, accessibilityRole = "button", ref: viewRef, testID }: Banner) => { const { onPressIn, onPressOut, scaleAnimatedStyle } = useScaleAnimation("medium"); const { themeType } = useIOThemeContext(); const theme = useIOTheme(); // Dynamic colors const colorTitle: IOColors = themeType === "dark" ? "grey-50" : "blueIO-850"; const colorCloseButton: IconButton["color"] = themeType === "dark" ? "contrast" : "neutral"; const colorMainButton = themeType === "dark" ? "blueIO-200" : theme["interactiveElem-default"]; const dynamicContainerStyles: ViewStyle = { backgroundColor: themeType === "dark" ? hexToRgba(IOColors[mapBackgroundColorDarkMode[color]], 0.1) : IOColors[mapBackgroundColorLightMode[color]] }; /* Generates a complete fallbackAccessibilityLabel by concatenating the title, content, and action if they are present. */ const fallbackAccessibilityLabel = [title, content, action] .filter(Boolean) .join(" "); const renderMainBlock = () => ( <> {title &&
{title}
} {content && ( {content} )} {action && ( /* Disable pointer events to avoid pressed state on the button */ {action} )}
{onClose && labelClose && ( )} ); const PressableButton = () => ( {renderMainBlock()} ); const StaticComponent = () => ( {renderMainBlock()} ); return action ? : ; };