import * as stylex from '@stylexjs/stylex'; import React from 'react'; import type { IconComponent } from '../../icons/glyph'; import { CheckmarkCircle } from '../../icons/glyphs/checkmark-circle'; import { ExclamationCircle } from '../../icons/glyphs/exclamation-circle'; import { InformationCircle } from '../../icons/glyphs/information-circle'; import { XCircle } from '../../icons/glyphs/x-circle'; import { useRender } from '../../primitives/utils'; import type { MosaicComponentProps } from '../../props'; import { mergeStyleProps, themeProps } from '../../props'; import { reset } from '../../styles/reset.styles'; import { Icon } from '../icon'; import { iconColors, rootColors, styles } from './banner.styles'; type BannerColor = 'neutral' | 'warning' | 'negative' | 'positive'; const DEFAULT_COLOR: BannerColor = 'neutral'; const ICONS: Record = { neutral: InformationCircle, warning: ExclamationCircle, negative: XCircle, positive: CheckmarkCircle, }; const BannerColorContext = React.createContext(DEFAULT_COLOR); /** Props for the banner surface, including native `div` props and the Mosaic `render` escape hatch. */ export interface BannerRootProps extends MosaicComponentProps<'div'> { /** Semantic colour of the fill, border, icon, and copy. @default 'neutral' */ color?: BannerColor; } const Root = React.forwardRef(function MosaicBannerRoot( { color = DEFAULT_COLOR, render, xstyle, children, ...rest }, ref, ) { const element = useRender({ defaultTagName: 'div', render, ref, props: { ...mergeStyleProps( themeProps('banner-root', { color }), stylex.props(reset.base, styles.root, rootColors[color], xstyle), rest, ), children: ( <>
{children}
), }, }); return {element}; }); /** Props for the banner's headline. */ export type BannerLabelProps = MosaicComponentProps<'p'>; const Label = React.forwardRef(function MosaicBannerLabel( { render, xstyle, ...rest }, ref, ) { const color = React.useContext(BannerColorContext); return useRender({ defaultTagName: 'p', render, ref, props: { ...mergeStyleProps(themeProps('banner-label', { color }), stylex.props(reset.base, styles.label, xstyle), rest), }, }); }); /** Props for the banner's supporting copy. */ export type BannerDescriptionProps = MosaicComponentProps<'p'>; const Description = React.forwardRef(function MosaicBannerDescription( { render, xstyle, ...rest }, ref, ) { const color = React.useContext(BannerColorContext); return useRender({ defaultTagName: 'p', render, ref, props: { ...mergeStyleProps( themeProps('banner-description', { color }), stylex.props(reset.base, styles.description, xstyle), rest, ), }, }); }); /** * A tinted strip that annotates the surface it sits on with a status message. Composed through * `Banner.Root`, `Banner.Label`, and `Banner.Description`; every part accepts the Mosaic `render` * prop and forwards its ref. `Banner.Root` renders the icon for its `color` itself. * * `Banner.Root` sets no ARIA role. A banner that appears in response to something the user did * should be given `role='status'` (or `role='alert'` for an error) so it is announced. * * @example * * Error banner * Renew now to avoid service interruption. * */ export const Banner = { Root, Label, Description };