{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "property-section",
  "type": "registry:component",
  "title": "Property Section",
  "description": "Compact key/value grid for inspector panels — labels, sublabels, optional collapse.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/property-section/property-section.tsx",
      "content": "\"use client\";\n\nimport {\n  type ComponentPropsWithoutRef,\n  forwardRef,\n  type ReactNode,\n} from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\n/**\n * One row in the property grid.\n *\n * @public\n */\nexport type PropertyEntry = {\n  /** Stable id (used as React key + analytics hook). */\n  id: string;\n  /** Property label (left column). */\n  label: ReactNode;\n  /** Optional sublabel rendered beneath the label. */\n  sublabel?: ReactNode;\n  /** Property value (right column). */\n  value: ReactNode;\n};\n\n/**\n * Localizable strings.\n *\n * @public\n */\nexport type PropertySectionLabels = {\n  /** Aria-label for the section. Defaults to `\"Properties\"`. */\n  region?: string;\n};\n\nconst DEFAULT_LABELS = {\n  region: \"Properties\",\n} as const satisfies Required<PropertySectionLabels>;\n\n/**\n * Props for {@link PropertySection}.\n *\n * @public\n */\nexport type PropertySectionProps = {\n  /** Optional collapsible affordance. Pass `false` to render the body inline (default). */\n  collapsible?: boolean;\n  /** Property entries in render order. */\n  entries: PropertyEntry[];\n  /** Localizable strings. */\n  labels?: PropertySectionLabels;\n  /** Optional title rendered as a small uppercase heading above the grid. */\n  title?: ReactNode;\n} & ComponentPropsWithoutRef<\"section\">;\n\nconst Grid = (props: { entries: PropertyEntry[] }): React.ReactElement => (\n  <dl\n    className=\"grid grid-cols-[max-content_1fr] gap-x-4 gap-y-1.5 px-3 pb-3 pt-1 text-xs\"\n    data-property-grid\n  >\n    {props.entries.map((entry) => (\n      <div className=\"contents\" key={entry.id}>\n        <dt\n          className=\"flex flex-col text-muted-foreground\"\n          data-property-id={entry.id}\n        >\n          <span>{entry.label}</span>\n          {entry.sublabel ? (\n            <span className=\"text-[10px] uppercase tracking-wide text-muted-foreground/70\">\n              {entry.sublabel}\n            </span>\n          ) : null}\n        </dt>\n        <dd className=\"text-right text-foreground\">{entry.value}</dd>\n      </div>\n    ))}\n  </dl>\n);\n\nconst Body = (props: {\n  collapsible: boolean;\n  entries: PropertyEntry[];\n  title?: ReactNode;\n}): React.ReactElement => {\n  const grid = <Grid entries={props.entries} />;\n  if (!props.title) {\n    return grid;\n  }\n  if (props.collapsible) {\n    return (\n      <details className=\"group\" open>\n        <summary\n          className=\"flex cursor-pointer items-center justify-between gap-2 px-3 py-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground\"\n          data-property-summary\n        >\n          <span>{props.title}</span>\n          <span\n            aria-hidden=\"true\"\n            className=\"text-muted-foreground/70 group-open:rotate-90\"\n          >\n            ›\n          </span>\n        </summary>\n        {grid}\n      </details>\n    );\n  }\n  return (\n    <>\n      <header\n        className=\"flex items-center justify-between gap-2 border-b border-border px-3 py-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground\"\n        data-property-header\n      >\n        {props.title}\n      </header>\n      {grid}\n    </>\n  );\n};\n\n/**\n * Compact key / value grid for an inspector panel. Use one\n * `PropertySection` per logical group (Identity, Layout, State, etc.)\n * so the right dock stays scannable. Pure presentation — the host\n * computes the entries from the current selection.\n *\n * @example\n * ```tsx\n * <PropertySection\n *   title=\"Layout\"\n *   entries={[\n *     { id: \"x\", label: \"X\", value: \"120\" },\n *     { id: \"y\", label: \"Y\", value: \"80\" },\n *     { id: \"size\", label: \"Size\", value: \"240 × 120\" },\n *   ]}\n * />\n * ```\n *\n * @public\n */\nexport const PropertySection = forwardRef<HTMLElement, PropertySectionProps>(\n  (props, ref) => {\n    const {\n      className,\n      collapsible = false,\n      entries,\n      labels,\n      title,\n      ...rest\n    } = props;\n    const resolvedLabels = { ...DEFAULT_LABELS, ...labels };\n    return (\n      <section\n        aria-label={resolvedLabels.region}\n        className={cn(\n          \"rounded-lg border bg-background text-foreground\",\n          className,\n        )}\n        data-property-section\n        ref={ref}\n        {...rest}\n      >\n        <Body collapsible={collapsible} entries={entries} title={title} />\n      </section>\n    );\n  },\n);\nPropertySection.displayName = \"PropertySection\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
