{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "pricing-matrix-grouped",
  "title": "Pricing — Grouped Matrix",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/pricing-matrix-grouped/pricing-matrix-grouped.component.ts",
      "content": "// Feature comparison matrix with collapsible capability groups, so a long plan\n// comparison opens on the summary rows and expands only where the reader is looking.\n// Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core'\nimport { Check, ChevronDown, LucideAngularModule, Minus } from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\n\nexport interface PricingGroupedRow {\n  feature: string\n  values: (boolean | string)[]\n}\n\nexport interface PricingGroupedGroup {\n  id: string\n  label: string\n  summary: string\n  rows: PricingGroupedRow[]\n}\n\nconst plans = ['Team', 'Business', 'Enterprise']\n\nconst groups: PricingGroupedGroup[] = [\n  {\n    id: 'modelling',\n    label: 'Modelling',\n    summary: 'Definitions, versioning, and review',\n    rows: [\n      { feature: 'Certified definitions', values: ['50', '500', 'Unlimited'] },\n      { feature: 'Version history', values: ['30 days', 'Unlimited', 'Unlimited'] },\n      { feature: 'Required review', values: [false, true, true] },\n      { feature: 'Period locking', values: [false, true, true] },\n    ],\n  },\n  {\n    id: 'access',\n    label: 'Access & identity',\n    summary: 'Who sees which rows',\n    rows: [\n      { feature: 'SSO (SAML)', values: [true, true, true] },\n      { feature: 'SCIM provisioning', values: [false, true, true] },\n      { feature: 'Row-level scope', values: [false, true, true] },\n      { feature: 'Customer-managed keys', values: [false, false, true] },\n    ],\n  },\n  {\n    id: 'ops',\n    label: 'Operations',\n    summary: 'Cost, alerting, and support',\n    rows: [\n      { feature: 'Query budgets', values: [false, true, true] },\n      { feature: 'Drift alerting', values: [false, true, true] },\n      { feature: 'Audit log export', values: [false, '90 days', 'Unlimited'] },\n      { feature: 'Named support engineer', values: [false, false, true] },\n    ],\n  },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-pricing-matrix-grouped, [ui-pricing-matrix-grouped]',\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    UiCardComponent,\n    UiCardContentComponent,\n    UiSeparatorComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableComponent,\n    UiTableRowComponent,\n  ],\n  template: `\n    <section data-slot=\"pricing-matrix-grouped\" [class]=\"rootClass\">\n      <div class=\"mx-auto max-w-4xl px-6 py-20 lg:py-28\">\n        <span ui-badge variant=\"secondary\">Compare plans</span>\n        <h2 class=\"mt-4 text-3xl font-semibold tracking-tight sm:text-4xl\">Grouped, so it stays readable</h2>\n\n        <div ui-card class=\"mt-8\">\n          <div ui-card-content class=\"p-0\">\n            <div\n              class=\"text-muted-foreground grid grid-cols-[1fr_repeat(3,5rem)] gap-2 px-4 py-3 text-xs font-medium sm:grid-cols-[1fr_repeat(3,7rem)]\"\n            >\n              <span>Capability</span>\n              @for (plan of plans; track plan) {\n                <span class=\"text-center\">{{ plan }}</span>\n              }\n            </div>\n            <ui-separator />\n\n            @for (group of groups; track group.id) {\n              <div>\n                <button\n                  type=\"button\"\n                  class=\"hover:bg-muted focus-visible:ring-ring flex w-full items-center gap-3 px-4 py-3 text-left transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                  [attr.aria-expanded]=\"isOpen(group.id)\"\n                  [attr.aria-controls]=\"'group-' + group.id\"\n                  (click)=\"toggle(group.id)\"\n                >\n                  <lucide-icon [img]=\"ChevronDown\" [class]=\"chevronClass(group.id)\" aria-hidden=\"true\" />\n                  <span class=\"min-w-0\">\n                    <span class=\"block text-sm font-medium\">{{ group.label }}</span>\n                    <span class=\"text-muted-foreground block text-xs\">{{ group.summary }}</span>\n                  </span>\n                  <span class=\"text-muted-foreground ml-auto shrink-0 font-mono text-xs\">\n                    {{ group.rows.length }}\n                  </span>\n                </button>\n\n                @if (isOpen(group.id)) {\n                  <div [id]=\"'group-' + group.id\">\n                    <table ui-table>\n                      <tbody ui-table-body>\n                        @for (row of group.rows; track row.feature) {\n                          <tr ui-table-row>\n                            <td ui-table-cell class=\"pl-11 text-sm\">{{ row.feature }}</td>\n                            @for (value of row.values; track $index) {\n                              <td ui-table-cell class=\"w-20 text-center sm:w-28\">\n                                @if (value === true) {\n                                  <lucide-icon [img]=\"Check\" class=\"text-success mx-auto size-4\" aria-hidden=\"true\" />\n                                  <span class=\"sr-only\">Included</span>\n                                } @else if (value === false) {\n                                  <lucide-icon\n                                    [img]=\"Minus\"\n                                    class=\"text-muted-foreground/50 mx-auto size-4\"\n                                    aria-hidden=\"true\"\n                                  />\n                                  <span class=\"sr-only\">Not included</span>\n                                } @else {\n                                  <span class=\"font-mono text-xs\">{{ value }}</span>\n                                }\n                              </td>\n                            }\n                          </tr>\n                        }\n                      </tbody>\n                    </table>\n                  </div>\n                }\n                <ui-separator />\n              </div>\n            }\n          </div>\n        </div>\n\n        <button ui-button class=\"mt-6\">Start on Business</button>\n      </div>\n    </section>\n  `,\n})\nexport class UiPricingMatrixGroupedComponent {\n  protected readonly Check = Check\n  protected readonly ChevronDown = ChevronDown\n  protected readonly Minus = Minus\n\n  readonly plans = plans\n  readonly groups = groups\n\n  @Input() set initialOpen(v: string[] | undefined) {\n    if (v) this.open.set([...v])\n  }\n  @Input('class') className?: string\n\n  // Opens on the first group only: a fully expanded matrix is the thing readers\n  // bounce off, and every group is one click from open.\n  readonly open = signal<string[]>([groups[0].id])\n\n  get rootClass(): string {\n    return cn('bg-background', this.className)\n  }\n\n  isOpen(id: string): boolean {\n    return this.open().includes(id)\n  }\n\n  chevronClass(id: string): string {\n    return cn('text-muted-foreground size-4 shrink-0 transition-transform', !this.isOpen(id) && '-rotate-90')\n  }\n\n  toggle(id: string): void {\n    this.open.update((current) => (current.includes(id) ? current.filter((entry) => entry !== id) : [...current, id]))\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/pricing-matrix-grouped/pricing-matrix-grouped.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/pricing-matrix-grouped/index.ts",
      "content": "export {\n  UiPricingMatrixGroupedComponent,\n  type PricingGroupedGroup,\n  type PricingGroupedRow,\n} from './pricing-matrix-grouped.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/pricing-matrix-grouped/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/card.json",
    "https://uipkge.dev/r/angular/separator.json",
    "https://uipkge.dev/r/angular/table.json"
  ],
  "description": "Feature comparison matrix with collapsible capability groups, so a long plan comparison opens on the summary rows and expands only where the reader is looking.",
  "categories": [
    "marketing"
  ],
  "deprecated": true,
  "replacedBy": "pricing"
}