{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "banner",
  "title": "Banner",
  "description": "Page-wide message at the top of a page, such as planned maintenance or an outage, that can be dismissed.",
  "dependencies": [
    "@base-ui/react@^1.8.0",
    "class-variance-authority@^0.7.1",
    "lucide-react@^1.48.0"
  ],
  "registryDependencies": [
    "https://rdf.dev.360online.com/r/button.json"
  ],
  "files": [
    {
      "path": "registry/rapid/ui/banner/banner.tsx",
      "content": "'use client'\n\nimport { mergeProps } from '@base-ui/react/merge-props'\nimport { useRender } from '@base-ui/react/use-render'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { CircleAlertIcon, CircleCheckIcon, InfoIcon, TriangleAlertIcon, XIcon } from 'lucide-react'\nimport * as React from 'react'\nimport { Button } from '@/components/ui/button'\nimport { cn } from '@/lib/utils'\n\n// Each variant has its own tint and icon colour. The variant is also carried by the icon and the screen reader prefix,\n// never by colour alone.\nconst bannerVariants = cva(\n\t'group/banner w-full border-b text-sm text-foreground [--banner-max-width:var(--container-7xl)]',\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tinfo: 'border-info-border bg-info-subtle [&_[data-slot=banner-icon]]:text-info-subtle-foreground',\n\t\t\t\twarning: 'border-warning-border bg-warning-subtle [&_[data-slot=banner-icon]]:text-warning-subtle-foreground',\n\t\t\t\tdestructive:\n\t\t\t\t\t'border-destructive-border bg-destructive-subtle [&_[data-slot=banner-icon]]:text-destructive-subtle-foreground',\n\t\t\t\tsuccess: 'border-success-border bg-success-subtle [&_[data-slot=banner-icon]]:text-success-subtle-foreground',\n\t\t\t},\n\t\t},\n\t\tdefaultVariants: {\n\t\t\tvariant: 'info',\n\t\t},\n\t},\n)\n\ntype BannerVariant = NonNullable<VariantProps<typeof bannerVariants>['variant']>\n\nconst defaultIcons: Record<BannerVariant, React.ElementType> = {\n\tinfo: InfoIcon,\n\twarning: TriangleAlertIcon,\n\tdestructive: CircleAlertIcon,\n\tsuccess: CircleCheckIcon,\n}\n\nconst defaultLabels: Record<BannerVariant, string> = {\n\tinfo: 'Information',\n\twarning: 'Warning',\n\tdestructive: 'Error',\n\tsuccess: 'Success',\n}\n\nfunction Banner({\n\tclassName,\n\tvariant = 'info',\n\trole = 'status',\n\ticon,\n\tlabel,\n\topen: openProp,\n\tdefaultOpen = true,\n\tonOpenChange,\n\tonDismiss,\n\tdismissible,\n\tcloseLabel = 'Dismiss',\n\tchildren,\n\t...props\n}: Omit<React.ComponentProps<'div'>, 'role'> &\n\tVariantProps<typeof bannerVariants> & {\n\t\t/**\n\t\t * `status` (default) is announced politely when the message appears after the page has loaded. Use `alert` only for\n\t\t * urgent messages, such as an ongoing outage; screen readers interrupt the user to read it.\n\t\t */\n\t\trole?: 'status' | 'alert'\n\t\t/** Replace the variant's icon, or pass `false` to hide it */\n\t\ticon?: React.ReactNode\n\t\t/**\n\t\t * Screen reader prefix that names the kind of message, such as \"Warning\". Defaults to an English label for the\n\t\t * variant; pass a translation, or `false` when the title already says it.\n\t\t */\n\t\tlabel?: string | false\n\t\t/** Controlled visibility */\n\t\topen?: boolean\n\t\t/** Initial visibility when uncontrolled */\n\t\tdefaultOpen?: boolean\n\t\t/** Called with the new visibility when the banner is dismissed */\n\t\tonOpenChange?: (open: boolean) => void\n\t\t/** Called when the user dismisses the banner. Passing it shows the dismiss button */\n\t\tonDismiss?: () => void\n\t\t/** Show the dismiss button. Defaults to `true` when `onDismiss` or `onOpenChange` is passed */\n\t\tdismissible?: boolean\n\t\t/** Accessible name of the dismiss button */\n\t\tcloseLabel?: string\n\t}) {\n\tconst [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n\tconst open = openProp ?? uncontrolledOpen\n\tconst showDismiss = dismissible ?? (onDismiss !== undefined || onOpenChange !== undefined)\n\tconst resolvedVariant: BannerVariant = variant ?? 'info'\n\n\tif (!open) return null\n\n\tconst Icon = defaultIcons[resolvedVariant]\n\tconst prefix = label === false ? null : (label ?? defaultLabels[resolvedVariant])\n\n\tfunction dismiss() {\n\t\tif (openProp === undefined) setUncontrolledOpen(false)\n\t\tonOpenChange?.(false)\n\t\tonDismiss?.()\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tdata-slot=\"banner\"\n\t\t\tdata-variant={resolvedVariant}\n\t\t\trole={role}\n\t\t\tclassName={cn(bannerVariants({ variant: resolvedVariant }), className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t<div\n\t\t\t\tdata-slot=\"banner-container\"\n\t\t\t\tclassName=\"mx-auto flex w-full max-w-(--banner-max-width) items-start gap-3 px-4 py-2.5 sm:px-6\"\n\t\t\t>\n\t\t\t\t{icon === false ? null : (\n\t\t\t\t\t<span\n\t\t\t\t\t\tdata-slot=\"banner-icon\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\tclassName=\"flex h-5 shrink-0 items-center [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{icon ?? <Icon />}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t\t<div data-slot=\"banner-content\" className=\"flex min-w-0 flex-1 flex-wrap items-baseline gap-x-3 gap-y-1 leading-5\">\n\t\t\t\t\t{prefix ? <span className=\"sr-only\">{`${prefix}: `}</span> : null}\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t\t{showDismiss ? (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tdata-slot=\"banner-close\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\t\taria-label={closeLabel}\n\t\t\t\t\t\tonClick={dismiss}\n\t\t\t\t\t\tclassName=\"-my-1.5 -me-2 hover:bg-foreground/10 dark:hover:bg-foreground/10\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<XIcon aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n\nfunction BannerTitle({ className, ...props }: React.ComponentProps<'p'>) {\n\treturn <p data-slot=\"banner-title\" className={cn('font-medium', className)} {...props} />\n}\n\nfunction BannerDescription({ className, ...props }: React.ComponentProps<'p'>) {\n\treturn (\n\t\t<p\n\t\t\tdata-slot=\"banner-description\"\n\t\t\tclassName={cn('min-w-0 text-pretty wrap-break-word [&_a]:underline [&_a]:underline-offset-4', className)}\n\t\t\t{...props}\n\t\t/>\n\t)\n}\n\n// Renders a link by default. Use `render={<button type=\"button\" />}` for an action that is not navigation.\nfunction BannerAction({ className, render, ...props }: useRender.ComponentProps<'a'>) {\n\treturn useRender({\n\t\tdefaultTagName: 'a',\n\t\tprops: mergeProps<'a'>(\n\t\t\t{\n\t\t\t\tclassName: cn(\n\t\t\t\t\t'inline-flex min-h-6 cursor-pointer items-center gap-1 rounded-sm font-medium whitespace-nowrap underline underline-offset-4 outline-none hover:decoration-2 focus-visible:ring-3 focus-visible:ring-ring [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n\t\t\t\t\tclassName,\n\t\t\t\t),\n\t\t\t},\n\t\t\tprops,\n\t\t),\n\t\trender,\n\t\tstate: {\n\t\t\tslot: 'banner-action',\n\t\t},\n\t})\n}\n\nexport { Banner, BannerTitle, BannerDescription, BannerAction, bannerVariants }\n",
      "type": "registry:ui",
      "target": "components/ui/banner.tsx"
    }
  ],
  "meta": {
    "new": true
  },
  "type": "registry:ui"
}