{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "feature-cards-media",
  "title": "Feature Cards Media",
  "description": "Two-column feature cards with independent media and optional icons.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "badge",
    "card"
  ],
  "files": [
    {
      "path": "payload-components/source/blocks/shared/featureFields.ts",
      "content": "import type { Field } from 'payload'\n\n/**\n * Shared field core for the Feature component family.\n *\n * Every feature variant (feature-grid-basic, feature-split, feature-bento,\n * feature-steps, …) spreads these section-heading fields first and then\n * appends its own variant-specific shape — the item array and CTA links,\n * which differ per layout. Editing the shared eyebrow/title/description here\n * updates every installed feature block at once, so the family never drifts\n * field-by-field across a repo.\n *\n * Installed once per repo at `src/blocks/shared/featureFields.ts`; re-running\n * `payload-components add feature-*` never overwrites a copy you have already edited.\n */\nexport const featureFields: Field[] = [\n  {\n    name: 'eyebrow',\n    type: 'text',\n  },\n  {\n    name: 'title',\n    type: 'text',\n    required: true,\n  },\n  {\n    name: 'description',\n    type: 'textarea',\n  },\n]\n",
      "type": "registry:file",
      "target": "~/src/blocks/shared/featureFields.ts"
    },
    {
      "path": "payload-components/source/blocks/shared/featureIcons.ts",
      "content": "import type { LucideIcon } from 'lucide-react'\nimport type { Field } from 'payload'\n\nimport { ChartBarIncreasing, Database, Fingerprint, IdCard, Shield, Zap } from 'lucide-react'\n\n/**\n * Shared icon allowlist for Feature variants with editor-selected icons.\n *\n * Payload stores the stable string key while the frontend resolves the matching\n * Lucide component. The field factory lets each variant choose whether an icon\n * is optional without duplicating the editor options or rendering map.\n */\nexport const featureIconOptions = [\n  'chart',\n  'database',\n  'fingerprint',\n  'id-card',\n  'shield',\n  'zap',\n] as const\n\nexport type FeatureIconName = (typeof featureIconOptions)[number]\n\nexport function createFeatureIconField(required = false): Field {\n  return {\n    name: 'icon',\n    type: 'select',\n    options: [\n      { label: 'Chart', value: 'chart' },\n      { label: 'Database', value: 'database' },\n      { label: 'Fingerprint', value: 'fingerprint' },\n      { label: 'ID card', value: 'id-card' },\n      { label: 'Shield', value: 'shield' },\n      { label: 'Zap', value: 'zap' },\n    ],\n    required,\n  }\n}\n\nexport const featureIcons: Record<FeatureIconName, LucideIcon> = {\n  chart: ChartBarIncreasing,\n  database: Database,\n  fingerprint: Fingerprint,\n  'id-card': IdCard,\n  shield: Shield,\n  zap: Zap,\n}\n",
      "type": "registry:file",
      "target": "~/src/blocks/shared/featureIcons.ts"
    },
    {
      "path": "payload-components/source/blocks/FeatureCardsMedia/config.ts",
      "content": "import type { Block } from 'payload'\n\nimport { featureFields } from '@/blocks/shared/featureFields'\nimport { createFeatureIconField } from '@/blocks/shared/featureIcons'\nimport { linkGroup } from '@/fields/linkGroup'\n\nexport const FeatureCardsMedia: Block = {\n  slug: 'featureCardsMedia',\n  // Existing apps must migrate stored data before adopting this identifier:\n  // https://www.payload-components.xyz/docs/registry#installed-source-and-migrations\n  dbName: 'pc_feat_card_med',\n  interfaceName: 'FeatureCardsMediaBlock',\n  fields: [\n    // Shared feature core (eyebrow, title, description). Variant-specific fields\n    // follow; edit the shared shape in @/blocks/shared/featureFields.\n    ...featureFields,\n    {\n      name: 'items',\n      type: 'array',\n      required: true,\n      minRows: 2,\n      maxRows: 4,\n      admin: {\n        initCollapsed: true,\n      },\n      fields: [\n        {\n          name: 'title',\n          type: 'text',\n          required: true,\n        },\n        {\n          name: 'description',\n          type: 'textarea',\n          required: true,\n        },\n        createFeatureIconField(),\n        {\n          name: 'image',\n          type: 'upload',\n          relationTo: 'media',\n          required: true,\n        },\n      ],\n    },\n    linkGroup({\n      overrides: {\n        admin: {\n          initCollapsed: true,\n        },\n        maxRows: 2,\n      },\n    }),\n  ],\n  labels: {\n    plural: 'Feature Cards Media Blocks',\n    singular: 'Feature Cards Media',\n  },\n}\n",
      "type": "registry:file",
      "target": "~/src/blocks/FeatureCardsMedia/config.ts"
    },
    {
      "path": "payload-components/source/blocks/FeatureCardsMedia/Component.tsx",
      "content": "import React from 'react'\n\nimport type { FeatureCardsMediaBlock as FeatureCardsMediaBlockData } from '@/payload-types'\nimport type { FeatureIconName } from '@/blocks/shared/featureIcons'\n\nimport { featureIcons } from '@/blocks/shared/featureIcons'\nimport { CMSLink } from '@/components/Link'\nimport { Media } from '@/components/Media'\nimport { Badge } from '@/components/ui/badge'\nimport { Card, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\nimport { cn } from '@/utilities/ui'\n\n// Layout adapted from tailark/blocks (MIT) — re-implemented as a Payload block.\n\ntype Props = FeatureCardsMediaBlockData & {\n  id?: string\n  className?: string\n  disableInnerContainer?: boolean\n}\n\nexport const FeatureCardsMediaBlock: React.FC<Props> = ({\n  className,\n  description,\n  disableInnerContainer,\n  eyebrow,\n  id,\n  items,\n  links,\n  title,\n}) => {\n  return (\n    <section className={cn('container', className)} id={id ? `block-${id}` : undefined}>\n      <div className=\"overflow-hidden rounded-frame border border-border/70 bg-card/35 px-6 py-10 sm:px-8 lg:px-12 lg:py-14\">\n        <div\n          className={cn('flex flex-col gap-10', {\n            'mx-auto max-w-6xl': !disableInnerContainer,\n          })}\n        >\n          <div className=\"flex max-w-3xl flex-col gap-4\">\n            {eyebrow ? (\n              <Badge variant=\"outline\" className=\"w-fit rounded-full px-3 py-1 uppercase tracking-eyebrow\">\n                {eyebrow}\n              </Badge>\n            ) : null}\n\n            <h2 className=\"text-4xl font-medium tracking-display text-balance sm:text-5xl\">{title}</h2>\n\n            {description ? (\n              <p className=\"text-base leading-7 text-muted-foreground sm:text-lg\">{description}</p>\n            ) : null}\n          </div>\n\n          {items && items.length > 0 ? (\n            <div className=\"grid gap-5 md:grid-cols-2\">\n              {items.map((item, index) => {\n                const Icon =\n                  item.icon && item.icon in featureIcons\n                    ? featureIcons[item.icon as FeatureIconName]\n                    : undefined\n\n                return (\n                  <Card\n                    key={item.id ?? `${item.title}-${index}`}\n                    className=\"overflow-hidden border-border/70 bg-background/85 py-0 shadow-none\"\n                  >\n                    <div className=\"aspect-video overflow-hidden border-b border-border/70 bg-muted\">\n                      {item.image ? (\n                        <Media resource={item.image} imgClassName=\"h-full w-full object-cover\" />\n                      ) : null}\n                    </div>\n                    <CardHeader className=\"gap-4 p-6\">\n                      <div className=\"flex items-center gap-3\">\n                        {Icon ? (\n                          <span className=\"flex size-10 shrink-0 items-center justify-center rounded-full border border-border/70 bg-muted\">\n                            <Icon className=\"size-5 text-foreground\" aria-hidden=\"true\" />\n                          </span>\n                        ) : null}\n                        <CardTitle className=\"text-xl tracking-title\">{item.title}</CardTitle>\n                      </div>\n                      <CardDescription className=\"text-sm leading-7 text-muted-foreground\">\n                        {item.description}\n                      </CardDescription>\n                    </CardHeader>\n                  </Card>\n                )\n              })}\n            </div>\n          ) : null}\n\n          {links && links.length > 0 ? (\n            <div className=\"flex flex-col gap-3 sm:flex-row\">\n              {links.map(({ link }, index) => (\n                <CMSLink\n                  key={index}\n                  appearance={link.appearance === 'outline' ? 'outline' : 'default'}\n                  {...link}\n                />\n              ))}\n            </div>\n          ) : null}\n        </div>\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:file",
      "target": "~/src/blocks/FeatureCardsMedia/Component.tsx"
    }
  ],
  "meta": {
    "payloadComponent": {
      "installCommand": "payload-components add feature-cards-media",
      "postInstall": [
        "generate:types",
        "generate:importmap"
      ],
      "requiresPayloadComponentWrapper": true,
      "supportedTargets": [
        "payload-website-starter",
        "payload-blocks-app"
      ]
    }
  },
  "docs": "Payload Components installs this as a Payload CMS block for the Payload website starter shape.\n\nRecommended install:\n\n```bash\npnpm payload-components add feature-cards-media\n```\n\nDirect shadcn install URL:\n\n```bash\npnpm dlx shadcn@latest add https://www.payload-components.xyz/r/feature-cards-media.json\n```\n\nDirect shadcn installs only copy the block source files and shadcn UI dependencies. Use `payload-components add feature-cards-media` when you also want Payload layout registration, `RenderBlocks` wiring, `generate:types`, and `generate:importmap` handled for you.",
  "type": "registry:block"
}