{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "logo-ticker-infinite",
  "title": "Logo Ticker Infinite",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/logo-ticker-infinite/logo-ticker-infinite.component.ts",
      "content": "// Infinite marquee brand partner ticker with sector category filters, speed and\n// pause controls, and interactive telemetry hover cards. Port of the Vue/React\n// block 1:1 — the track repeats the filtered list twice for a seamless loop;\n// hover a tile to inspect its live integration telemetry.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport { LucideAngularModule, Pause, Play } from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\n\nexport interface TickerBrandPartner {\n  id: string\n  name: string\n  category: 'devtools' | 'fintech' | 'enterprise'\n  role: string\n  metric: string\n  region: string\n}\n\nexport type LogoTickerCategory = 'all' | 'devtools' | 'fintech' | 'enterprise'\n\nconst partners: TickerBrandPartner[] = [\n  {\n    id: 'vercel',\n    name: 'Vercel Edge',\n    category: 'devtools',\n    role: 'Global Edge Ingestion',\n    metric: '99.95% SLA',\n    region: 'Global',\n  },\n  {\n    id: 'supabase',\n    name: 'Supabase Postgres',\n    category: 'devtools',\n    role: 'Real-Time Replication',\n    metric: '1.2M QPS',\n    region: 'US-East',\n  },\n  {\n    id: 'linear',\n    name: 'Linear Method',\n    category: 'enterprise',\n    role: 'Issue Sync Engine',\n    metric: '< 20ms Sync',\n    region: 'Global',\n  },\n  {\n    id: 'stripe',\n    name: 'Stripe Billing',\n    category: 'fintech',\n    role: 'Usage Metering Gateway',\n    metric: '$120M+ Processed',\n    region: 'Global',\n  },\n  {\n    id: 'resend',\n    name: 'Resend SMTP',\n    category: 'devtools',\n    role: 'Transactional Dispatch',\n    metric: '200M msgs/mo',\n    region: 'US-West',\n  },\n  {\n    id: 'cloudflare',\n    name: 'Cloudflare Workers',\n    category: 'devtools',\n    role: 'Zero-Trust Isolation',\n    metric: '300+ Cities',\n    region: 'Global',\n  },\n  {\n    id: 'planetscale',\n    name: 'PlanetScale DB',\n    category: 'enterprise',\n    role: 'Branching Schema',\n    metric: 'Zero Downtime',\n    region: 'EU-Central',\n  },\n  {\n    id: 'brex',\n    name: 'Brex Treasury',\n    category: 'fintech',\n    role: 'Ledger Reconciliation',\n    metric: 'Real-time API',\n    region: 'US-East',\n  },\n]\n\nconst categoryFilters: LogoTickerCategory[] = ['all', 'devtools', 'fintech', 'enterprise']\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-logo-ticker-infinite, [ui-logo-ticker-infinite]',\n  standalone: true,\n  // React renders the root <section> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [LucideAngularModule, UiBadgeComponent],\n  styles: [\n    `\n      @keyframes ui-logo-ticker-marquee {\n        0% {\n          transform: translateX(0%);\n        }\n        100% {\n          transform: translateX(-50%);\n        }\n      }\n      .animate-marquee {\n        animation: ui-logo-ticker-marquee 32s linear infinite;\n      }\n    `,\n  ],\n  template: `\n    <section data-slot=\"logo-ticker-infinite\" [class]=\"rootClass\">\n      <div class=\"mx-auto max-w-7xl space-y-8 text-center\">\n        <!-- Section Header & Category Filter Controls -->\n        <div\n          class=\"border-border/60 flex flex-col items-center justify-between gap-4 border-b pb-6 text-left md:flex-row\"\n        >\n          <div class=\"space-y-1\">\n            <div class=\"flex items-center gap-2\">\n              <span class=\"bg-success size-2 rounded-full\"></span>\n              <p class=\"text-foreground font-mono text-xs font-semibold tracking-wider uppercase\">\n                Production Verified Infrastructure\n              </p>\n            </div>\n            <p class=\"text-muted-foreground text-xs\">\n              Trusted by modern engineering teams shipping high-throughput production workloads.\n            </p>\n          </div>\n\n          <!-- Filter & Control Buttons -->\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <!-- Category Filter Pills -->\n            <div class=\"bg-background border-border flex items-center gap-1 rounded-lg border p-0.5\">\n              @for (cat of filters; track cat) {\n                <button type=\"button\" [class]=\"filterPillClass(cat)\" (click)=\"setCategory(cat)\">\n                  {{ cat }}\n                </button>\n              }\n            </div>\n\n            <!-- Marquee Speed / Pause Controls -->\n            <div class=\"flex items-center gap-1\">\n              <button\n                type=\"button\"\n                class=\"border-border bg-background text-muted-foreground hover:text-foreground rounded-lg border p-1.5 transition-colors\"\n                [attr.title]=\"isPaused() ? 'Resume Ticker' : 'Pause Ticker'\"\n                [attr.aria-label]=\"isPaused() ? 'Resume Ticker' : 'Pause Ticker'\"\n                (click)=\"togglePaused()\"\n              >\n                @if (isPaused()) {\n                  <lucide-icon [img]=\"Play\" class=\"size-3.5 fill-current\" />\n                } @else {\n                  <lucide-icon [img]=\"Pause\" class=\"size-3.5\" />\n                }\n              </button>\n              <button\n                type=\"button\"\n                class=\"border-border bg-background text-muted-foreground hover:text-foreground rounded-lg border px-2 py-1 font-mono text-xs transition-colors\"\n                [attr.aria-label]=\"isFastSpeed() ? 'Normal speed' : 'Double speed'\"\n                (click)=\"toggleSpeed()\"\n              >\n                {{ isFastSpeed() ? '2x' : '1x' }}\n              </button>\n            </div>\n          </div>\n        </div>\n\n        <!-- Infinite Marquee Track Viewport with Gradient Fade Edges -->\n        <div class=\"group relative overflow-hidden\">\n          <!-- Left & Right Gradient Shadows -->\n          <div\n            class=\"from-background via-background/80 pointer-events-none absolute inset-y-0 left-0 z-10 w-16 bg-gradient-to-r to-transparent sm:w-28\"\n          ></div>\n          <div\n            class=\"from-background via-background/80 pointer-events-none absolute inset-y-0 right-0 z-10 w-16 bg-gradient-to-l to-transparent sm:w-28\"\n          ></div>\n\n          <!-- Double Track Container for Seamless Infinite Scrolling -->\n          <div [class]=\"trackClass\" [style.animation-play-state]=\"isPaused() ? 'paused' : 'running'\">\n            <!-- Repeat partners array twice for infinite seamless loop -->\n            @for (item of loopedPartners(); track item.id + '-' + item.index) {\n              <div\n                class=\"border-border bg-card/80 hover:bg-card hover:border-primary/50 relative flex shrink-0 cursor-pointer items-center gap-3 rounded-xl border px-4 py-2.5 shadow-xs backdrop-blur-md transition-colors duration-200\"\n                (mouseenter)=\"setHovered(item.partner)\"\n                (mouseleave)=\"clearHovered()\"\n              >\n                <div\n                  class=\"bg-primary/10 text-primary flex size-7 items-center justify-center rounded-lg font-mono text-xs font-bold\"\n                >\n                  {{ item.partner.name.charAt(0) }}\n                </div>\n\n                <div class=\"text-left\">\n                  <p class=\"text-foreground font-mono text-xs font-semibold tracking-tight\">{{ item.partner.name }}</p>\n                  <p class=\"text-muted-foreground text-xs\">{{ item.partner.role }}</p>\n                </div>\n\n                <span\n                  ui-badge\n                  variant=\"outline\"\n                  class=\"border-success/20 bg-success/10 text-success ml-1 font-mono text-xs\"\n                >\n                  {{ item.partner.metric }}\n                </span>\n              </div>\n            }\n          </div>\n        </div>\n\n        <!-- Live Hover Telemetry Card -->\n        @if (hoveredBrand(); as hovered) {\n          <div\n            class=\"border-border bg-card animate-in fade-in-0 mx-auto max-w-md rounded-xl border p-3 text-left shadow-lg duration-150\"\n          >\n            <div class=\"flex items-center justify-between font-mono text-xs\">\n              <span class=\"text-foreground font-bold\">{{ hovered.name }} Architecture Integration</span>\n              <span class=\"text-success\">{{ hovered.region }}</span>\n            </div>\n            <p class=\"text-muted-foreground mt-1 text-xs\">\n              Connected through {{ hovered.role }} &bull; Monitored with {{ hovered.metric }} verified telemetry.\n            </p>\n          </div>\n        }\n      </div>\n    </section>\n  `,\n})\nexport class UiLogoTickerInfiniteComponent {\n  protected readonly Pause = Pause\n  protected readonly Play = Play\n\n  protected readonly filters = categoryFilters\n\n  @Input('class') className?: string\n  @Input() set initialCategory(v: LogoTickerCategory | undefined) {\n    if (v) this.selectedCategory.set(v)\n  }\n\n  readonly selectedCategory = signal<LogoTickerCategory>('all')\n  readonly isPaused = signal(false)\n  readonly isFastSpeed = signal(false)\n  readonly hoveredBrand = signal<TickerBrandPartner | null>(null)\n\n  readonly filteredPartners = computed(() => {\n    const selected = this.selectedCategory()\n    if (selected === 'all') return partners\n    return partners.filter((p) => p.category === selected)\n  })\n\n  /** The filtered list twice, so the -50% marquee loop is seamless. */\n  readonly loopedPartners = computed(() => {\n    const list = this.filteredPartners()\n    return [...list, ...list].map((partner, index) => ({ partner, index }))\n  })\n\n  get rootClass(): string {\n    return cn(\n      'border-border/80 bg-muted/20 relative overflow-hidden border-y px-4 py-14 sm:px-6 sm:py-20 lg:px-8',\n      this.className,\n    )\n  }\n\n  get trackClass(): string {\n    return cn(\n      'flex w-max items-center gap-4 py-3',\n      !this.isPaused() && 'animate-marquee',\n      this.isFastSpeed() ? '[animation-duration:15s]' : '[animation-duration:32s]',\n    )\n  }\n\n  filterPillClass(cat: LogoTickerCategory): string {\n    return cn(\n      'rounded-md px-2.5 py-1 font-mono text-xs capitalize transition-colors',\n      this.selectedCategory() === cat\n        ? 'bg-primary text-primary-foreground font-semibold shadow-xs'\n        : 'text-muted-foreground hover:text-foreground',\n    )\n  }\n\n  setCategory(cat: LogoTickerCategory): void {\n    this.selectedCategory.set(cat)\n  }\n\n  togglePaused(): void {\n    this.isPaused.update((v) => !v)\n  }\n\n  toggleSpeed(): void {\n    this.isFastSpeed.update((v) => !v)\n  }\n\n  setHovered(partner: TickerBrandPartner): void {\n    this.hoveredBrand.set(partner)\n  }\n\n  clearHovered(): void {\n    this.hoveredBrand.set(null)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/logo-ticker-infinite/logo-ticker-infinite.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/logo-ticker-infinite/index.ts",
      "content": "export {\n  UiLogoTickerInfiniteComponent,\n  type TickerBrandPartner,\n  type LogoTickerCategory,\n} from './logo-ticker-infinite.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/logo-ticker-infinite/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json"
  ],
  "description": "Infinite marquee brand partner ticker with sector category filters, speed and pause controls, and interactive telemetry hover cards.",
  "categories": [
    "marketing"
  ],
  "deprecated": true,
  "replacedBy": "logos"
}