{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "integrations-logo-grid",
  "title": "Integrations — Logo Grid",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/integrations-logo-grid/integrations-logo-grid.component.ts",
      "content": "// Integration marquee grid: category chips filter a wall of square logo tiles\n// with hover labels, over a count line and a request-an-integration prompt.\n// Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, 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'\nimport { UiToggleGroupComponent, UiToggleGroupItemComponent } from '@/ui/toggle-group/toggle-group.component'\n\ninterface LogoIntegration {\n  name: string\n  mark: string\n  category: string\n}\n\n// Monogram stand-ins: swap the `mark` for a real logo file per integration.\nconst integrations: LogoIntegration[] = [\n  { name: 'Snowflake', mark: 'SF', category: 'Warehouses' },\n  { name: 'BigQuery', mark: 'BQ', category: 'Warehouses' },\n  { name: 'Redshift', mark: 'RS', category: 'Warehouses' },\n  { name: 'Databricks', mark: 'DB', category: 'Warehouses' },\n  { name: 'Postgres', mark: 'PG', category: 'Warehouses' },\n  { name: 'Okta', mark: 'OK', category: 'Identity' },\n  { name: 'Entra ID', mark: 'EI', category: 'Identity' },\n  { name: 'Google Workspace', mark: 'GW', category: 'Identity' },\n  { name: 'Slack', mark: 'SL', category: 'Alerting' },\n  { name: 'PagerDuty', mark: 'PD', category: 'Alerting' },\n  { name: 'Opsgenie', mark: 'OG', category: 'Alerting' },\n  { name: 'dbt', mark: 'DT', category: 'Modelling' },\n  { name: 'Airflow', mark: 'AF', category: 'Modelling' },\n  { name: 'Dagster', mark: 'DG', category: 'Modelling' },\n  { name: 'Fivetran', mark: 'FT', category: 'Modelling' },\n  { name: 'Looker', mark: 'LK', category: 'Downstream' },\n  { name: 'Hex', mark: 'HX', category: 'Downstream' },\n  { name: 'Sigma', mark: 'SG', category: 'Downstream' },\n]\n\nconst ALL = 'All'\nconst categories: string[] = [ALL, ...new Set(integrations.map((i) => i.category))]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-integrations-logo-grid, [ui-integrations-logo-grid]',\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: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiToggleGroupComponent,\n    UiToggleGroupItemComponent,\n  ],\n  template: `\n    <section data-slot=\"integrations-logo-grid\" [class]=\"rootClass\">\n      <div class=\"mx-auto max-w-6xl px-6 py-20 lg:py-28\">\n        <div class=\"flex flex-wrap items-end justify-between gap-6\">\n          <div class=\"max-w-2xl\">\n            <span ui-badge variant=\"secondary\">Integrations</span>\n            <h2 class=\"mt-4 text-3xl font-semibold tracking-tight sm:text-4xl\">Works with what you already run</h2>\n            <p class=\"text-muted-foreground mt-3 text-lg\">\n              Read-only connections, configured once. Nothing here needs an agent installed on your side.\n            </p>\n          </div>\n\n          <div\n            ui-toggle-group\n            type=\"single\"\n            variant=\"outline\"\n            size=\"sm\"\n            class=\"flex-nowrap overflow-x-auto\"\n            aria-label=\"Filter integrations by category\"\n            [value]=\"active()\"\n            (valueChange)=\"onCategory($event)\"\n          >\n            @for (category of allCategories; track category) {\n              <button ui-toggle-group-item [value]=\"category\">{{ category }}</button>\n            }\n          </div>\n        </div>\n\n        <div\n          class=\"bg-border border-border mt-10 grid grid-cols-2 gap-px overflow-hidden rounded-xl border sm:grid-cols-4 lg:grid-cols-6\"\n        >\n          @for (integration of visible(); track integration.name) {\n            <div\n              class=\"bg-background hover:bg-muted/60 group flex aspect-square flex-col items-center justify-center gap-2 p-4 transition-colors\"\n            >\n              <span\n                class=\"border-border text-muted-foreground group-hover:text-foreground flex size-11 items-center justify-center rounded-lg border font-mono text-sm font-semibold transition-colors\"\n                aria-hidden=\"true\"\n              >\n                {{ integration.mark }}\n              </span>\n              <span class=\"text-muted-foreground group-hover:text-foreground text-center text-xs transition-colors\">\n                {{ integration.name }}\n              </span>\n            </div>\n          }\n        </div>\n\n        <div class=\"border-border mt-6 flex flex-wrap items-center justify-between gap-4 border-t pt-5\">\n          <p class=\"text-muted-foreground text-sm\">\n            {{ visible().length }} of {{ total }} integrations shown · more added every release.\n          </p>\n          <button ui-button variant=\"ghost\" size=\"sm\">\n            Request an integration\n            <lucide-icon [img]=\"ArrowRight\" class=\"ml-1.5 size-3.5\" aria-hidden=\"true\" />\n          </button>\n        </div>\n      </div>\n    </section>\n  `,\n})\nexport class UiIntegrationsLogoGridComponent {\n  protected readonly ArrowRight = ArrowRight\n\n  protected readonly allCategories = categories\n  protected readonly total = integrations.length\n\n  @Input('class') className?: string\n\n  readonly active = signal<string>(ALL)\n\n  readonly visible = computed(() => {\n    const current = this.active()\n    return current === ALL ? integrations : integrations.filter((i) => i.category === current)\n  })\n\n  get rootClass(): string {\n    return cn('bg-background', this.className)\n  }\n\n  onCategory(value: string | string[]): void {\n    const next = Array.isArray(value) ? value[0] : value\n    // Single-select emits '' when the pressed item is pressed again;\n    // fall back to ALL so the wall is never empty.\n    this.active.set(next || ALL)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/integrations-logo-grid/integrations-logo-grid.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/integrations-logo-grid/index.ts",
      "content": "export { UiIntegrationsLogoGridComponent } from './integrations-logo-grid.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/integrations-logo-grid/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/toggle-group.json"
  ],
  "description": "Integration marquee grid: category chips filter a wall of square logo tiles with hover labels, over a count line and a request-an-integration prompt.",
  "categories": [
    "marketing"
  ]
}