{
	"$schema": "https://shadcn-svelte.com/schema/registry-item.json",
	"name": "banner",
	"title": "Banner",
	"type": "registry:ui",
	"description": "A dismissible page-level notice that remembers when it was closed.",
	"dependencies": [
		"@lucide/svelte@^0.554.0"
	],
	"devDependencies": [
		"@lucide/svelte@^0.544.0",
		"tailwind-variants@^3.1.1"
	],
	"files": [
		{
			"content": "<script lang=\"ts\" module>\r\n\timport { tv, type VariantProps } from 'tailwind-variants';\r\n\r\n\texport type BannerPersist = 'local' | 'session' | 'none';\r\n\r\n\texport const bannerVariants = tv({\r\n\t\tbase: 'relative flex w-full items-center gap-3 border-b px-4 py-2.5 text-sm',\r\n\t\tvariants: {\r\n\t\t\tvariant: {\r\n\t\t\t\tdefault: 'bg-muted text-foreground border-border',\r\n\t\t\t\tprimary: 'bg-primary text-primary-foreground border-primary',\r\n\t\t\t\tinfo: 'bg-blue-500/10 text-blue-900 border-blue-500/20 dark:text-blue-200',\r\n\t\t\t\tsuccess: 'bg-emerald-500/10 text-emerald-900 border-emerald-500/20 dark:text-emerald-200',\r\n\t\t\t\twarning: 'bg-amber-500/10 text-amber-900 border-amber-500/20 dark:text-amber-200',\r\n\t\t\t\tdestructive: 'bg-destructive/10 text-destructive border-destructive/20'\r\n\t\t\t},\r\n\t\t\talign: {\r\n\t\t\t\tstart: 'justify-start',\r\n\t\t\t\tcenter: 'justify-center text-center'\r\n\t\t\t}\r\n\t\t},\r\n\t\tdefaultVariants: {\r\n\t\t\tvariant: 'default',\r\n\t\t\talign: 'start'\r\n\t\t}\r\n\t});\r\n\r\n\texport type BannerVariant = VariantProps<typeof bannerVariants>['variant'];\r\n\texport type BannerAlign = VariantProps<typeof bannerVariants>['align'];\r\n\r\n\texport const bannerStorageKey = (id: string) => `more-shadcn:banner:${id}`;\r\n\r\n\tfunction storageFor(persist: BannerPersist): Storage | null {\r\n\t\tif (persist === 'none' || typeof window === 'undefined') return null;\r\n\t\ttry {\r\n\t\t\treturn persist === 'session' ? window.sessionStorage : window.localStorage;\r\n\t\t} catch {\r\n\t\t\t// Storage can throw in private mode or when blocked by the browser.\r\n\t\t\treturn null;\r\n\t\t}\r\n\t}\r\n\r\n\texport function isBannerDismissed(id: string, persist: BannerPersist = 'local') {\r\n\t\treturn storageFor(persist)?.getItem(bannerStorageKey(id)) === 'dismissed';\r\n\t}\r\n\r\n\texport function dismissBanner(id: string, persist: BannerPersist = 'local') {\r\n\t\tstorageFor(persist)?.setItem(bannerStorageKey(id), 'dismissed');\r\n\t}\r\n\r\n\t/** Clears a stored dismissal so the banner shows again. */\r\n\texport function resetBanner(id: string, persist: BannerPersist = 'local') {\r\n\t\tstorageFor(persist)?.removeItem(bannerStorageKey(id));\r\n\t}\r\n</script>\r\n\r\n<script lang=\"ts\">\r\n\timport { cn } from '$UTILS$';\r\n\timport { X } from '@lucide/svelte';\r\n\timport { slide } from 'svelte/transition';\r\n\timport type { Snippet } from 'svelte';\r\n\r\n\tlet {\r\n\t\tid = 'banner',\r\n\t\topen = $bindable(true),\r\n\t\tpersist = 'local',\r\n\t\tvariant = 'default',\r\n\t\talign = 'start',\r\n\t\tdismissible = true,\r\n\t\tsticky = false,\r\n\t\tcloseLabel = 'Dismiss',\r\n\t\tclass: className,\r\n\t\tondismiss,\r\n\t\ticon,\r\n\t\taction,\r\n\t\tchildren,\r\n\t\t...rest\r\n\t}: {\r\n\t\tid?: string;\r\n\t\topen?: boolean;\r\n\t\tpersist?: BannerPersist;\r\n\t\tvariant?: BannerVariant;\r\n\t\talign?: BannerAlign;\r\n\t\tdismissible?: boolean;\r\n\t\tsticky?: boolean;\r\n\t\tcloseLabel?: string;\r\n\t\tclass?: string;\r\n\t\tondismiss?: () => void;\r\n\t\ticon?: Snippet;\r\n\t\taction?: Snippet;\r\n\t\tchildren: Snippet;\r\n\t\t[key: string]: unknown;\r\n\t} = $props();\r\n\r\n\t// With persistence the banner stays hidden until the stored value is read,\r\n\t// otherwise a dismissed banner would flash on every page load.\r\n\tlet ready = $state(persist === 'none');\r\n\r\n\t$effect(() => {\r\n\t\tif (ready) return;\r\n\t\tif (isBannerDismissed(id, persist)) open = false;\r\n\t\tready = true;\r\n\t});\r\n\r\n\tfunction dismiss() {\r\n\t\topen = false;\r\n\t\tdismissBanner(id, persist);\r\n\t\tondismiss?.();\r\n\t}\r\n</script>\r\n\r\n{#if ready && open}\r\n\t<div\r\n\t\t{id}\r\n\t\trole=\"region\"\r\n\t\taria-label=\"Banner\"\r\n\t\ttransition:slide={{ duration: 200 }}\r\n\t\tclass={cn(bannerVariants({ variant, align }), sticky && 'sticky top-0 z-50', className)}\r\n\t\t{...rest}\r\n\t>\r\n\t\t{#if icon}\r\n\t\t\t<span class=\"flex shrink-0 items-center\">{@render icon()}</span>\r\n\t\t{/if}\r\n\r\n\t\t<div class={cn('min-w-0 flex-1', align === 'center' && 'text-center')}>\r\n\t\t\t{@render children()}\r\n\t\t</div>\r\n\r\n\t\t{#if action}\r\n\t\t\t<div class=\"flex shrink-0 items-center gap-2\">{@render action()}</div>\r\n\t\t{/if}\r\n\r\n\t\t{#if dismissible}\r\n\t\t\t<button\r\n\t\t\t\ttype=\"button\"\r\n\t\t\t\tonclick={dismiss}\r\n\t\t\t\taria-label={closeLabel}\r\n\t\t\t\tclass={cn(\r\n\t\t\t\t\t'ml-1 shrink-0 rounded-sm p-1 opacity-70 transition-opacity hover:opacity-100',\r\n\t\t\t\t\t'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\r\n\t\t\t\t\talign === 'center' && 'absolute right-3 top-1/2 -translate-y-1/2'\r\n\t\t\t\t)}\r\n\t\t\t>\r\n\t\t\t\t<X class=\"size-4\" />\r\n\t\t\t</button>\r\n\t\t{/if}\r\n\t</div>\r\n{/if}\r\n",
			"type": "registry:ui",
			"target": "banner/banner.svelte"
		},
		{
			"content": "import Banner, {\r\n\tbannerVariants,\r\n\tbannerStorageKey,\r\n\tisBannerDismissed,\r\n\tdismissBanner,\r\n\tresetBanner,\r\n\ttype BannerAlign,\r\n\ttype BannerPersist,\r\n\ttype BannerVariant\r\n} from './banner.svelte';\r\n\r\nexport {\r\n\tBanner,\r\n\tBanner as Root,\r\n\tbannerVariants,\r\n\tbannerStorageKey,\r\n\tisBannerDismissed,\r\n\tdismissBanner,\r\n\tresetBanner,\r\n\ttype BannerAlign,\r\n\ttype BannerPersist,\r\n\ttype BannerVariant\r\n};\r\n",
			"type": "registry:ui",
			"target": "banner/index.ts"
		}
	]
}