{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "announcement-rotating-strip",
  "title": "Announcement — Rotating Strip",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/announcement-rotating-strip/announcement-rotating-strip.component.ts",
      "content": "// Announcement bar that cycles through several messages on a timer, pausing on\n// hover and on focus, with dot controls for stepping between them manually. Port of\n// the Vue/React block 1:1. Edit `messages` to change the rotation; `interval` sets\n// the dwell time per message in milliseconds.\nimport { ChangeDetectionStrategy, Component, DestroyRef, Input, OnDestroy, OnInit, inject, signal } from '@angular/core'\nimport { ArrowRight, LucideAngularModule } from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\n\nexport interface RotatingAnnouncement {\n  label: string\n  text: string\n  cta: string\n}\n\nconst messages: RotatingAnnouncement[] = [\n  { label: 'New', text: 'Row-level scoping now evaluates against SCIM groups.', cta: 'Changelog' },\n  { label: 'Event', text: 'Live walkthrough of the five-week rollout, every Thursday.', cta: 'Save a seat' },\n  { label: 'Docs', text: 'The reconciliation checklist for your first close is published.', cta: 'Read it' },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-announcement-rotating-strip, [ui-announcement-rotating-strip]',\n  standalone: true,\n  // The block renders its own root <div>: keep this wrapper out of the layout.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [LucideAngularModule, UiBadgeComponent, UiButtonComponent],\n  template: `\n    <div\n      data-slot=\"announcement-rotating-strip\"\n      class=\"border-border bg-muted/60 border-b backdrop-blur\"\n      (mouseenter)=\"paused.set(true)\"\n      (mouseleave)=\"paused.set(false)\"\n      (focusin)=\"paused.set(true)\"\n      (focusout)=\"paused.set(false)\"\n    >\n      <div class=\"mx-auto flex h-11 max-w-6xl items-center gap-3 px-6 text-sm\">\n        <!-- aria-live announces the rotation to screen readers without stealing focus. -->\n        <div class=\"flex min-w-0 flex-1 items-center gap-3\" aria-live=\"polite\" aria-atomic=\"true\">\n          <span ui-badge variant=\"secondary\" class=\"shrink-0\">{{ messages[index()].label }}</span>\n          <p class=\"min-w-0 truncate\">{{ messages[index()].text }}</p>\n          <button ui-button variant=\"link\" size=\"sm\" class=\"hidden h-auto shrink-0 p-0 text-xs sm:inline-flex\">\n            {{ messages[index()].cta }}\n            <lucide-icon [img]=\"ArrowRight\" [class]=\"'ml-1 size-3'\" aria-hidden=\"true\" />\n          </button>\n        </div>\n\n        <div class=\"flex shrink-0 items-center gap-1.5\">\n          @for (message of messages; track message.text; let i = $index) {\n            <button\n              type=\"button\"\n              [class]=\"dotClass(i)\"\n              [attr.aria-label]=\"'Show announcement ' + (i + 1) + ' of ' + messages.length\"\n              [attr.aria-current]=\"i === index() ? 'true' : null\"\n              (click)=\"goTo(i)\"\n            ></button>\n          }\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiAnnouncementRotatingStripComponent implements OnInit, OnDestroy {\n  @Input() interval = 6000\n\n  readonly index = signal(0)\n  readonly paused = signal(false)\n  readonly messages = messages\n\n  protected readonly ArrowRight = ArrowRight\n\n  private timer: ReturnType<typeof setInterval> | undefined\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => this.stop())\n  }\n\n  ngOnInit(): void {\n    this.start()\n  }\n\n  ngOnDestroy(): void {\n    this.stop()\n  }\n\n  advance(): void {\n    this.index.update((i) => (i + 1) % messages.length)\n  }\n\n  goTo(i: number): void {\n    this.index.set(i)\n  }\n\n  start(): void {\n    this.stop()\n    this.timer = setInterval(() => {\n      if (!this.paused()) this.advance()\n    }, this.interval)\n  }\n\n  stop(): void {\n    clearInterval(this.timer)\n    this.timer = undefined\n  }\n\n  dotClass(i: number): string {\n    return cn(\n      'focus-visible:ring-ring size-1.5 rounded-full transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',\n      i === this.index() ? 'bg-foreground' : 'bg-muted-foreground/40 hover:bg-muted-foreground',\n    )\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/announcement-rotating-strip/announcement-rotating-strip.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/announcement-rotating-strip/index.ts",
      "content": "export {\n  UiAnnouncementRotatingStripComponent,\n  type RotatingAnnouncement,\n} from './announcement-rotating-strip.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/announcement-rotating-strip/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json"
  ],
  "description": "Announcement bar that cycles through several messages on a timer, pausing on hover and on focus, with dot controls for stepping between them manually.",
  "categories": [
    "marketing"
  ],
  "deprecated": true,
  "replacedBy": "announcement"
}