{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "accordion", "type": "registry:component", "title": "Accordion", "description": "Componente Concorde Accordion — un solo archivo, Accordion.tsx.", "dependencies": [ "react" ], "registryDependencies": [], "files": [ { "path": "components/Accordion.tsx", "type": "registry:file", "target": "components/Accordion.tsx", "content": "\"use client\";\n\n/**\n * Accordion — Generado por Concorde\n * Fuente: Figma VOYAGER · \"DetailAccordions\" (2007:16995)\n *\n * Card blanca (radio 4, sombra) con título (corchetes naranjas vía CardTitle,\n * editable) y un chevron en círculo naranja a la derecha. El chevron gira:\n * · cerrado → apunta hacia abajo\n * · abierto → apunta hacia arriba (rotate 180)\n * El contenido se expande/colapsa. Controlado o no controlado.\n * Paths/gradiente del chevron copiados tal cual del SVG.\n */\n\nimport { useId, useState } from \"react\";\nimport type { JSX, ReactNode } from \"react\";\nimport CardTitle from \"@/concorde/components/CardTitle\";\n\nexport interface AccordionProps {\n /** Título (editable) — default \"INFORMACIÓN GENERAL\". Acepta texto o nodos (p.ej. un dot + texto). */\n title?: ReactNode;\n /** Contenido que se expande/colapsa */\n children?: ReactNode;\n /** Abierto (controlado) */\n open?: boolean;\n /** Abierto inicial (no controlado) — default false */\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Tamaño del título en px (default 14) */\n titleSize?: number;\n className?: string;\n}\n\nconst STYLE_ID = \"concorde-accordion-styles\";\n\nconst ACCORDION_STYLES = `\n/* La card BLANCA + sombra + redondeo envuelven TODO (header + contenido), así el\n contenido abierto queda sobre fondo blanco dentro de la misma card. */\n.pacc {\n width: 100%;\n background: #ffffff;\n border-radius: 4px;\n box-shadow: rgba(0,0,0,0.1) 0px 0px 16px 4px;\n overflow: hidden;\n font-family: var(--vmc-font-display, \"Plus Jakarta Sans\", -apple-system, sans-serif);\n}\n.pacc__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n width: 100%;\n min-height: 66px;\n box-sizing: border-box;\n padding: 0 16px;\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n}\n/* Divisor entre header y contenido — solo visible cuando está abierto */\n.pacc--open .pacc__header { box-shadow: inset 0 -1px 0 #ECECEE; }\n.pacc__chevron {\n flex-shrink: 0;\n transition: transform 0.25s cubic-bezier(0.3, 0, 0, 1);\n}\n.pacc--open .pacc__chevron { transform: rotate(180deg); }\n.pacc__content {\n display: grid;\n grid-template-rows: 0fr;\n transition: grid-template-rows 0.25s ease;\n}\n.pacc--open .pacc__content { grid-template-rows: 1fr; }\n.pacc__content-inner { overflow: hidden; }\n.pacc__content-pad {\n padding: 16px 18px 18px;\n font-size: 14px;\n line-height: 1.6;\n color: #475569;\n}\n@media (prefers-reduced-motion: reduce) {\n .pacc__chevron, .pacc__content { transition: none; }\n}\n`;\n\nlet _stylesInjected = false;\n\n// ─── Chevron en círculo (gradiente naranja · copiado del SVG) ───────────────\n\nfunction Chevron({ gradId }: { gradId: string }): JSX.Element {\n return (\n \n \n \n \n \n \n \n \n \n \n \n );\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport default function Accordion({\n title = \"INFORMACIÓN GENERAL\",\n children,\n open,\n defaultOpen = false,\n onOpenChange,\n titleSize = 14,\n className = \"\",\n}: AccordionProps): JSX.Element {\n const uid = useId().replace(/:/g, \"-\");\n const controlled = open !== undefined;\n const [internal, setInternal] = useState(defaultOpen);\n const isOpen = controlled ? open : internal;\n\n if (typeof document !== \"undefined\" && !_stylesInjected) {\n if (!document.getElementById(STYLE_ID)) {\n const el = document.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = ACCORDION_STYLES;\n document.head.appendChild(el);\n }\n _stylesInjected = true;\n }\n\n function handleToggle(): void {\n const next = !isOpen;\n if (!controlled) setInternal(next);\n onOpenChange?.(next);\n }\n\n const cls = [\"pacc\", isOpen ? \"pacc--open\" : \"\", className].filter(Boolean).join(\" \");\n\n return (\n <>\n