{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "content-calendar-matrix",
  "title": "Content Calendar Matrix",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/content-calendar-matrix/content-calendar-matrix.component.ts",
      "content": "// Editorial publishing calendar and content matrix with pacing KPI summary cards,\n// channel filter tabs, month picker, publication status pipeline, author avatars, SEO\n// keywords, and contextual action menus. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport {\n  Calendar,\n  CalendarCheck,\n  CalendarDays,\n  CheckCircle2,\n  Clock,\n  FileText,\n  Layers,\n  LucideAngularModule,\n  Mail,\n  MoreHorizontal,\n  PenLine,\n  Pencil,\n  Plus,\n  Search,\n  Share2,\n  Trash2,\n  UserCheck,\n  Video,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'\nimport {\n  UiDropdownMenuComponent,\n  UiDropdownMenuContentComponent,\n  UiDropdownMenuItemComponent,\n  UiDropdownMenuLabelComponent,\n  UiDropdownMenuSeparatorComponent,\n  UiDropdownMenuTriggerComponent,\n} from '@/ui/dropdown-menu/dropdown-menu.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport {\n  UiSelectComponent,\n  UiSelectContentComponent,\n  UiSelectItemComponent,\n  UiSelectTriggerComponent,\n  UiSelectValueComponent,\n} from '@/ui/select/select.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\n\nexport type ContentChannel = 'all' | 'blog' | 'newsletter' | 'youtube' | 'social'\nexport type ContentStatus = 'published' | 'scheduled' | 'in_review' | 'draft'\n\nexport interface ContentItem {\n  id: string\n  title: string\n  channel: 'blog' | 'newsletter' | 'youtube' | 'social'\n  channelLabel: string\n  publishDate: string\n  publishTime: string\n  author: { name: string; role: string; avatar: string }\n  status: ContentStatus\n  statusLabel: string\n  keywords: string[]\n  format: string\n}\n\nconst contentItems: ContentItem[] = [\n  {\n    id: 'cnt-01',\n    title: 'Deep Dive: Headless Reka UI Primitives in Vue 3.5',\n    channel: 'blog',\n    channelLabel: 'Blog',\n    publishDate: 'Sep 04',\n    publishTime: '10:00 AM EST',\n    author: { name: 'Sarah Chen', role: 'Lead Developer Advocate', avatar: 'SC' },\n    status: 'published',\n    statusLabel: 'Published',\n    keywords: ['#vue3', '#reka-ui', '#frontend'],\n    format: 'Technical Guide',\n  },\n  {\n    id: 'cnt-02',\n    title: 'Building Zero-Runtime Design Systems with Tailwind CSS v4',\n    channel: 'youtube',\n    channelLabel: 'YouTube',\n    publishDate: 'Sep 08',\n    publishTime: '02:00 PM EST',\n    author: { name: 'Marcus Vance', role: 'Senior UI Engineer', avatar: 'MV' },\n    status: 'scheduled',\n    statusLabel: 'Scheduled',\n    keywords: ['#tailwindcss', '#design-systems', '#webdev'],\n    format: 'Video Tutorial',\n  },\n  {\n    id: 'cnt-03',\n    title: 'Frontend Weekly #142: Subgrid Mastery & CSS Anchor Positioning',\n    channel: 'newsletter',\n    channelLabel: 'Newsletter',\n    publishDate: 'Sep 12',\n    publishTime: '09:00 AM EST',\n    author: { name: 'Elena Rostova', role: 'Technical Editor', avatar: 'ER' },\n    status: 'scheduled',\n    statusLabel: 'Scheduled',\n    keywords: ['#css', '#newsletter', '#subgrid'],\n    format: 'Email Dispatch',\n  },\n  {\n    id: 'cnt-04',\n    title: 'Accessible Command Palettes & Keyboard Shortcuts Architecture',\n    channel: 'blog',\n    channelLabel: 'Blog',\n    publishDate: 'Sep 16',\n    publishTime: '11:30 AM EST',\n    author: { name: 'David Kim', role: 'Accessibility Specialist', avatar: 'DK' },\n    status: 'in_review',\n    statusLabel: 'In Review',\n    keywords: ['#a11y', '#cmdk', '#keyboard-nav'],\n    format: 'Deep Dive',\n  },\n  {\n    id: 'cnt-05',\n    title: 'Product Drop: 12 New Enterprise Blocks for Nuxt 4 & React',\n    channel: 'social',\n    channelLabel: 'Social',\n    publishDate: 'Sep 21',\n    publishTime: '04:00 PM EST',\n    author: { name: 'Aisha Patel', role: 'Product Marketing Lead', avatar: 'AP' },\n    status: 'in_review',\n    statusLabel: 'In Review',\n    keywords: ['#changelog', '#uipkge', '#react', '#vue'],\n    format: 'Social Campaign',\n  },\n  {\n    id: 'cnt-06',\n    title: 'Micro-Interactions & Spring Physics in Modern Web Applications',\n    channel: 'youtube',\n    channelLabel: 'YouTube',\n    publishDate: 'Sep 26',\n    publishTime: '01:15 PM EST',\n    author: { name: 'Lucas Meyer', role: 'Motion Designer', avatar: 'LM' },\n    status: 'draft',\n    statusLabel: 'Draft',\n    keywords: ['#animation', '#spring-physics', '#ui-motion'],\n    format: 'Interactive Workshop',\n  },\n]\n\nconst channelFilters: Array<{ value: ContentChannel; label: string }> = [\n  { value: 'all', label: 'All Channels' },\n  { value: 'blog', label: 'Blog' },\n  { value: 'newsletter', label: 'Newsletter' },\n  { value: 'youtube', label: 'YouTube' },\n  { value: 'social', label: 'Social' },\n]\n\nconst channelBadge: Record<string, string> = {\n  blog: 'bg-info/10 text-info border-info/20',\n  newsletter: 'bg-chart-1/10 text-chart-1 border-chart-1/20',\n  youtube: 'bg-destructive/10 text-destructive border-destructive/20',\n  social: 'bg-info/10 text-info border-info/20',\n}\n\nconst statusBadge: Record<ContentStatus, string> = {\n  published: 'bg-success/10 text-success border-success/20',\n  scheduled: 'bg-info/10 text-info border-info/20',\n  in_review: 'bg-warning/10 text-warning border-warning/20',\n  draft: 'bg-muted text-muted-foreground border-border',\n}\n\nconst statusDot: Record<ContentStatus, string> = {\n  published: 'bg-success',\n  scheduled: 'bg-info',\n  in_review: 'bg-warning',\n  draft: 'bg-muted-foreground',\n}\n\nconst statusLabel: Record<ContentStatus, string> = {\n  published: 'Published',\n  scheduled: 'Scheduled',\n  in_review: 'In Review',\n  draft: 'Draft',\n}\n\nconst channelLabel: Record<string, string> = {\n  blog: 'Blog',\n  newsletter: 'Newsletter',\n  youtube: 'YouTube',\n  social: 'Social',\n}\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-content-calendar-matrix, [ui-content-calendar-matrix]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiAvatarComponent,\n    UiAvatarFallbackComponent,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiDropdownMenuComponent,\n    UiDropdownMenuContentComponent,\n    UiDropdownMenuItemComponent,\n    UiDropdownMenuLabelComponent,\n    UiDropdownMenuSeparatorComponent,\n    UiDropdownMenuTriggerComponent,\n    UiInputComponent,\n    UiSelectComponent,\n    UiSelectContentComponent,\n    UiSelectItemComponent,\n    UiSelectTriggerComponent,\n    UiSelectValueComponent,\n    UiTableComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n  ],\n  template: `\n    <div data-slot=\"content-calendar-matrix\" [class]=\"rootClass\">\n      <!-- Header Section -->\n      <div class=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n        <div>\n          <div class=\"flex items-center gap-2\">\n            <h2 class=\"text-2xl font-bold tracking-tight\">{{ title }}</h2>\n            <span ui-badge variant=\"outline\" class=\"hidden sm:inline-flex\">Editorial Desk</span>\n          </div>\n          <p class=\"text-muted-foreground mt-1 text-sm\">{{ subtitle }}</p>\n        </div>\n\n        <div class=\"flex flex-wrap items-center gap-3\">\n          <!-- Month Selector -->\n          <ui-select [value]=\"selectedMonth()\" (valueChange)=\"selectedMonth.set($event)\">\n            <button ui-select-trigger class=\"w-[180px] shadow-xs\">\n              <lucide-icon [img]=\"CalendarDays\" class=\"text-muted-foreground mr-2 size-4\" />\n              <ui-select-value placeholder=\"Select month\" />\n            </button>\n            <ui-select-content>\n              <ui-select-item value=\"2026-08\">August 2026</ui-select-item>\n              <ui-select-item value=\"2026-09\">September 2026</ui-select-item>\n              <ui-select-item value=\"2026-10\">October 2026</ui-select-item>\n              <ui-select-item value=\"2026-11\">November 2026</ui-select-item>\n            </ui-select-content>\n          </ui-select>\n\n          <!-- New Content Item Primary Button -->\n          <button ui-button class=\"gap-2 shadow-xs\">\n            <lucide-icon [img]=\"Plus\" class=\"size-4\" />\n            New Content Item\n          </button>\n        </div>\n      </div>\n\n      <!-- 4 Editorial Pacing KPI Cards -->\n      <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n        <div ui-card class=\"shadow-xs\">\n          <div ui-card-content class=\"space-y-3 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">Total Planned Content</p>\n              <div class=\"text-primary bg-primary/10 flex size-8 items-center justify-center rounded-md\">\n                <lucide-icon [img]=\"Layers\" class=\"size-4\" />\n              </div>\n            </div>\n            <div>\n              <div class=\"flex items-baseline gap-1.5\">\n                <span class=\"text-2xl font-bold tracking-tight tabular-nums\">24</span>\n                <span class=\"text-muted-foreground text-xs font-medium\">Pieces this month</span>\n              </div>\n              <p class=\"text-muted-foreground mt-1 text-xs\">+6 pieces vs previous month</p>\n            </div>\n          </div>\n        </div>\n        <div ui-card class=\"shadow-xs\">\n          <div ui-card-content class=\"space-y-3 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">Scheduled for Publish</p>\n              <div class=\"text-info bg-info/10 flex size-8 items-center justify-center rounded-md\">\n                <lucide-icon [img]=\"CalendarCheck\" class=\"size-4\" />\n              </div>\n            </div>\n            <div>\n              <div class=\"flex items-baseline gap-1.5\">\n                <span class=\"text-2xl font-bold tracking-tight tabular-nums\">8</span>\n                <span class=\"text-muted-foreground text-xs font-medium\">items ready</span>\n              </div>\n              <p class=\"text-muted-foreground mt-1 text-xs\">Next drop: Sep 08 · 02:00 PM EST</p>\n            </div>\n          </div>\n        </div>\n        <div ui-card class=\"shadow-xs\">\n          <div ui-card-content class=\"space-y-3 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">In Review / Drafting</p>\n              <div class=\"text-warning bg-warning/10 flex size-8 items-center justify-center rounded-md\">\n                <lucide-icon [img]=\"PenLine\" class=\"size-4\" />\n              </div>\n            </div>\n            <div>\n              <div class=\"flex items-baseline gap-1.5\">\n                <span class=\"text-2xl font-bold tracking-tight tabular-nums\">12</span>\n                <span class=\"text-muted-foreground text-xs font-medium\">in progress</span>\n              </div>\n              <p class=\"text-muted-foreground mt-1 text-xs\">7 in editorial review · 5 in drafting</p>\n            </div>\n          </div>\n        </div>\n        <div ui-card class=\"shadow-xs\">\n          <div ui-card-content class=\"space-y-3 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">Published this Month</p>\n              <div class=\"text-success bg-success/10 flex size-8 items-center justify-center rounded-md\">\n                <lucide-icon [img]=\"CheckCircle2\" class=\"size-4\" />\n              </div>\n            </div>\n            <div>\n              <div class=\"flex items-baseline gap-1.5\">\n                <span class=\"text-2xl font-bold tracking-tight tabular-nums\">4</span>\n                <span class=\"text-muted-foreground text-xs font-medium\">live</span>\n              </div>\n              <p class=\"text-muted-foreground mt-1 text-xs\">100% on-schedule publishing rate</p>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <!-- Pipeline & Matrix Controls Bar -->\n      <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n        <!-- Channel Filters -->\n        <div class=\"flex flex-wrap items-center gap-1.5\">\n          @for (filter of channelFilters; track filter.value) {\n            <button\n              ui-button\n              [variant]=\"selectedChannel() === filter.value ? 'default' : 'outline'\"\n              size=\"sm\"\n              class=\"h-8 gap-1.5 text-xs shadow-xs\"\n              (click)=\"selectedChannel.set(filter.value)\"\n            >\n              <lucide-icon [img]=\"filterIcon(filter.value)\" class=\"size-3.5\" />\n              <span>{{ filter.label }}</span>\n            </button>\n          }\n        </div>\n\n        <!-- Keyword / Title Search Input -->\n        <div class=\"relative w-full sm:w-64\">\n          <lucide-icon\n            [img]=\"Search\"\n            class=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2\"\n          />\n          <ui-input\n            [value]=\"searchQuery()\"\n            (valueChange)=\"searchQuery.set($event)\"\n            type=\"text\"\n            placeholder=\"Filter by keyword or title...\"\n            class=\"h-8 pl-8 text-xs shadow-xs\"\n          />\n        </div>\n      </div>\n\n      <!-- Content Publishing Pipeline Table -->\n      <div class=\"bg-card overflow-x-auto rounded-lg border shadow-xs\">\n        <table ui-table>\n          <thead ui-table-header>\n            <tr ui-table-row>\n              <th ui-table-head class=\"w-[180px]\">Publish Date &amp; Time</th>\n              <th ui-table-head class=\"min-w-[280px]\">Content Title</th>\n              <th ui-table-head class=\"w-[120px]\">Channel</th>\n              <th ui-table-head class=\"min-w-[180px]\">Author</th>\n              <th ui-table-head class=\"w-[130px]\">Status</th>\n              <th ui-table-head class=\"min-w-[200px]\">SEO / Keywords</th>\n              <th ui-table-head class=\"w-12 text-right\">\n                <span class=\"sr-only\">Actions</span>\n              </th>\n            </tr>\n          </thead>\n          <tbody ui-table-body>\n            @for (item of filteredItems(); track item.id) {\n              <tr ui-table-row class=\"hover:bg-muted/50 transition-colors\">\n                <!-- Publish Date & Time -->\n                <td ui-table-cell class=\"align-middle\">\n                  <div class=\"flex items-center gap-1.5\">\n                    <lucide-icon [img]=\"Clock\" class=\"text-muted-foreground size-3.5 shrink-0\" />\n                    <span class=\"text-xs font-medium tabular-nums\"\n                      >{{ item.publishDate }} · {{ item.publishTime }}</span\n                    >\n                  </div>\n                </td>\n\n                <!-- Content Title -->\n                <td ui-table-cell class=\"align-middle\">\n                  <div class=\"space-y-0.5\">\n                    <p class=\"text-foreground text-sm leading-snug font-semibold\">{{ item.title }}</p>\n                    <p class=\"text-muted-foreground text-xs font-normal\">{{ item.format }}</p>\n                  </div>\n                </td>\n\n                <!-- Channel Badge -->\n                <td ui-table-cell class=\"align-middle\">\n                  <span ui-badge variant=\"outline\" [class]=\"channelBadgeClass(item.channel)\">\n                    <lucide-icon [img]=\"channelIcon(item.channel)\" class=\"size-3\" />\n                    {{ channelName(item.channel) }}\n                  </span>\n                </td>\n\n                <!-- Author (Avatar + Name) -->\n                <td ui-table-cell class=\"align-middle\">\n                  <div class=\"flex items-center gap-2.5\">\n                    <ui-avatar class=\"size-7\">\n                      <ui-avatar-fallback class=\"bg-primary/10 text-primary text-xs font-semibold\">\n                        {{ item.author.avatar }}\n                      </ui-avatar-fallback>\n                    </ui-avatar>\n                    <div class=\"min-w-0\">\n                      <p class=\"text-foreground truncate text-xs font-medium\">{{ item.author.name }}</p>\n                      <p class=\"text-muted-foreground truncate text-xs\">{{ item.author.role }}</p>\n                    </div>\n                  </div>\n                </td>\n\n                <!-- Status Badge -->\n                <td ui-table-cell class=\"align-middle\">\n                  <span ui-badge variant=\"outline\" [class]=\"statusBadgeClass(item.status)\">\n                    <span [class]=\"statusDotClass(item.status) + ' size-1.5 rounded-full'\"></span>\n                    {{ statusName(item.status) }}\n                  </span>\n                </td>\n\n                <!-- SEO / Target Keywords -->\n                <td ui-table-cell class=\"align-middle\">\n                  <div class=\"flex flex-wrap gap-1\">\n                    @for (keyword of item.keywords; track keyword) {\n                      <span\n                        class=\"bg-muted/60 border-border/50 text-muted-foreground inline-flex items-center rounded-md border px-1.5 py-0.5 font-mono text-xs\"\n                      >\n                        {{ keyword }}\n                      </span>\n                    }\n                  </div>\n                </td>\n\n                <!-- Actions Dropdown -->\n                <td ui-table-cell class=\"text-right align-middle\">\n                  <ui-dropdown-menu>\n                    <button\n                      ui-button\n                      ui-dropdown-menu-trigger\n                      variant=\"ghost\"\n                      size=\"icon-sm\"\n                      class=\"text-muted-foreground size-8\"\n                    >\n                      <lucide-icon [img]=\"MoreHorizontal\" class=\"size-4\" />\n                      <span class=\"sr-only\">Open actions for {{ item.title }}</span>\n                    </button>\n                    <ui-dropdown-menu-content align=\"end\" class=\"w-48\">\n                      <ui-dropdown-menu-label class=\"text-xs\">Manage Content</ui-dropdown-menu-label>\n                      <ui-dropdown-menu-separator />\n                      <ui-dropdown-menu-item class=\"gap-2 text-xs\">\n                        <lucide-icon [img]=\"Pencil\" class=\"mr-2 size-3.5\" />\n                        <span>Edit item</span>\n                      </ui-dropdown-menu-item>\n                      <ui-dropdown-menu-item class=\"gap-2 text-xs\">\n                        <lucide-icon [img]=\"Calendar\" class=\"mr-2 size-3.5\" />\n                        <span>Reschedule</span>\n                      </ui-dropdown-menu-item>\n                      <ui-dropdown-menu-item class=\"gap-2 text-xs\">\n                        <lucide-icon [img]=\"UserCheck\" class=\"mr-2 size-3.5\" />\n                        <span>Assign reviewer</span>\n                      </ui-dropdown-menu-item>\n                      <ui-dropdown-menu-separator />\n                      <ui-dropdown-menu-item class=\"text-destructive focus:text-destructive gap-2 text-xs\">\n                        <lucide-icon [img]=\"Trash2\" class=\"mr-2 size-3.5\" />\n                        <span>Delete item</span>\n                      </ui-dropdown-menu-item>\n                    </ui-dropdown-menu-content>\n                  </ui-dropdown-menu>\n                </td>\n              </tr>\n            }\n\n            @if (filteredItems().length === 0) {\n              <tr ui-table-row>\n                <td ui-table-cell [attr.colspan]=\"7\" class=\"text-muted-foreground h-32 text-center text-sm\">\n                  No content items found matching the selected channel filter or search query.\n                </td>\n              </tr>\n            }\n          </tbody>\n        </table>\n      </div>\n\n      <!-- Publishing Matrix Footer -->\n      <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n        <p class=\"text-muted-foreground text-xs\">\n          Showing <span class=\"text-foreground font-semibold tabular-nums\">{{ filteredItems().length }}</span> of\n          <span class=\"text-foreground font-semibold tabular-nums\">{{ totalItems }}</span> scheduled content pieces for\n          <span class=\"text-foreground font-medium\">September 2026</span>\n        </p>\n        <div class=\"flex items-center gap-2\">\n          <button ui-button variant=\"outline\" size=\"sm\" class=\"h-8 text-xs shadow-xs\" disabled>Previous</button>\n          <button ui-button variant=\"outline\" size=\"sm\" class=\"h-8 text-xs shadow-xs\">Next</button>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiContentCalendarMatrixComponent {\n  protected readonly Calendar = Calendar\n  protected readonly CalendarCheck = CalendarCheck\n  protected readonly CalendarDays = CalendarDays\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Clock = Clock\n  protected readonly FileText = FileText\n  protected readonly Layers = Layers\n  protected readonly Mail = Mail\n  protected readonly MoreHorizontal = MoreHorizontal\n  protected readonly PenLine = PenLine\n  protected readonly Pencil = Pencil\n  protected readonly Plus = Plus\n  protected readonly Search = Search\n  protected readonly Share2 = Share2\n  protected readonly Trash2 = Trash2\n  protected readonly UserCheck = UserCheck\n  protected readonly Video = Video\n\n  protected readonly channelFilters = channelFilters\n  protected readonly totalItems = contentItems.length\n\n  @Input() title = 'Editorial Content Calendar'\n  @Input() subtitle = 'Schedule, organize, and track cross-channel editorial pipelines and publishing milestones.'\n  @Input('class') className?: string\n\n  readonly selectedMonth = signal('2026-09')\n  readonly selectedChannel = signal<ContentChannel>('all')\n  readonly searchQuery = signal('')\n\n  readonly filteredItems = computed(() => {\n    const query = this.searchQuery().trim().toLowerCase()\n    const channel = this.selectedChannel()\n    return contentItems.filter((item) => {\n      const matchesChannel = channel === 'all' || item.channel === channel\n      const matchesQuery =\n        !query ||\n        item.title.toLowerCase().includes(query) ||\n        item.author.name.toLowerCase().includes(query) ||\n        item.keywords.some((k) => k.toLowerCase().includes(query))\n      return matchesChannel && matchesQuery\n    })\n  })\n\n  get rootClass(): string {\n    return cn('w-full space-y-6', this.className)\n  }\n\n  filterIcon(value: ContentChannel): unknown {\n    switch (value) {\n      case 'blog':\n        return FileText\n      case 'newsletter':\n        return Mail\n      case 'youtube':\n        return Video\n      case 'social':\n        return Share2\n      default:\n        return Layers\n    }\n  }\n\n  channelIcon(channel: string): unknown {\n    switch (channel) {\n      case 'blog':\n        return FileText\n      case 'newsletter':\n        return Mail\n      case 'youtube':\n        return Video\n      default:\n        return Share2\n    }\n  }\n\n  channelBadgeClass(channel: string): string {\n    return cn('gap-1 text-xs font-medium', channelBadge[channel] ?? '')\n  }\n\n  channelName(channel: string): string {\n    return channelLabel[channel] ?? channel\n  }\n\n  statusBadgeClass(status: ContentStatus): string {\n    return cn('gap-1.5 text-xs font-medium', statusBadge[status])\n  }\n\n  statusDotClass(status: ContentStatus): string {\n    return statusDot[status]\n  }\n\n  statusName(status: ContentStatus): string {\n    return statusLabel[status]\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/content-calendar-matrix/content-calendar-matrix.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/content-calendar-matrix/index.ts",
      "content": "export {\n  UiContentCalendarMatrixComponent,\n  type ContentChannel,\n  type ContentStatus,\n  type ContentItem,\n} from './content-calendar-matrix.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/content-calendar-matrix/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/avatar.json",
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/dropdown-menu.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/select.json",
    "https://uipkge.dev/r/angular/table.json"
  ],
  "description": "Editorial publishing calendar and content matrix with pacing KPI summary cards, channel filter tabs, month picker, publication status pipeline, author avatars, SEO keywords, and contextual action menus.",
  "categories": [
    "media",
    "app",
    "marketing"
  ]
}