{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "card", "title": "Card", "description": "Hand-drawn card with optional stacked paper effect.", "dependencies": [ "roughjs" ], "registryDependencies": [ "https://www.bydefaulthuman.fun/r/use-rough.json", "https://www.bydefaulthuman.fun/r/crumble-theme.json", "https://www.bydefaulthuman.fun/r/rough-lib.json", "utils" ], "files": [ { "path": "registry/new-york/ui/card.tsx", "content": "\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useRef,\n type CSSProperties,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n randomSeed,\n resolveRoughVars,\n type CrumbleColorProps,\n type CrumbleTheme,\n} from \"@/lib/rough\";\nimport { useRough } from \"@/hooks/use-rough\";\n\nexport interface CardProps\n extends HTMLAttributes,\n CrumbleColorProps {\n children: ReactNode;\n id?: string;\n padding?: number;\n stacked?: boolean;\n style?: CSSProperties;\n theme?: CrumbleTheme;\n}\n\nexport function Card({\n children,\n className,\n fill,\n id,\n onClick,\n padding = 20,\n stacked = false,\n stroke,\n strokeMuted,\n style,\n theme: themeProp,\n ...props\n}: CardProps) {\n const containerRef = useRef(null);\n const svgRef = useRef(null);\n const svg2Ref = useRef(null);\n const svg3Ref = useRef(null);\n const { animateOnHover, drawRect, theme } = useRough({\n stableId: id ?? \"card\",\n svgRef,\n theme: themeProp,\n variant: \"border\",\n });\n const roughStyle = resolveRoughVars({ stroke, strokeMuted, fill });\n\n const drawInto = useCallback(\n (\n svgEl: SVGSVGElement,\n width: number,\n height: number,\n options: {\n inset?: number;\n offsetX?: number;\n offsetY?: number;\n reseed?: boolean;\n rotate?: number;\n strokeOpacity?: number;\n } = {},\n ) => {\n const {\n inset = 2,\n offsetX = 0,\n offsetY = 0,\n reseed = false,\n rotate = 0,\n strokeOpacity = 1,\n } = options;\n\n svgEl.replaceChildren();\n svgEl.setAttribute(\"width\", String(width + Math.abs(offsetX) + 8));\n svgEl.setAttribute(\"height\", String(height + Math.abs(offsetY) + 8));\n\n const node = drawRect(\n inset + offsetX,\n inset + offsetY,\n width - inset * 2,\n height - inset * 2,\n {\n fill: fill ?? \"none\",\n seed: reseed ? randomSeed() : undefined,\n stroke: stroke ?? \"currentColor\",\n },\n );\n\n if (!node) return;\n\n if (strokeOpacity < 1) {\n node.style.opacity = String(strokeOpacity);\n }\n\n if (rotate !== 0) {\n const cx = width / 2 + offsetX;\n const cy = height / 2 + offsetY;\n node.setAttribute(\"transform\", `rotate(${rotate} ${cx} ${cy})`);\n }\n\n svgEl.appendChild(node);\n },\n [drawRect, fill, stroke],\n );\n\n const draw = useCallback(\n (reseed = false) => {\n const container = containerRef.current;\n const svg = svgRef.current;\n\n if (!container || !svg) return;\n\n const width = container.offsetWidth;\n const height = container.offsetHeight;\n svg.setAttribute(\"width\", String(width));\n svg.setAttribute(\"height\", String(height));\n\n drawInto(svg, width, height, { reseed });\n\n if (stacked) {\n if (svg2Ref.current) {\n drawInto(svg2Ref.current, width, height, {\n offsetX: 4,\n offsetY: 5,\n rotate: 1.5,\n strokeOpacity: 0.3,\n });\n }\n\n if (svg3Ref.current) {\n drawInto(svg3Ref.current, width, height, {\n offsetX: 8,\n offsetY: 9,\n rotate: 3,\n strokeOpacity: 0.15,\n });\n }\n }\n },\n [drawInto, stacked],\n );\n\n useEffect(() => {\n draw();\n }, [draw, theme]);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const observer = new ResizeObserver(() => draw());\n observer.observe(container);\n\n return () => observer.disconnect();\n }, [draw]);\n\n return (\n \n {stacked ? (\n <>\n \n \n \n ) : null}\n {\n if (animateOnHover) draw(true);\n }}\n onMouseLeave={() => {\n if (animateOnHover) draw(false);\n }}\n {...props}\n >\n \n
\n {children}\n
\n \n \n );\n}\n\nexport function CardHeader({\n children,\n className,\n ...props\n}: HTMLAttributes) {\n return (\n
\n {children}\n
\n );\n}\n\nexport function CardTitle({\n children,\n className,\n ...props\n}: HTMLAttributes) {\n return (\n \n {children}\n \n );\n}\n\nexport function CardDescription({\n children,\n className,\n ...props\n}: HTMLAttributes) {\n return (\n
\n {children}\n
\n );\n}\n\nexport function CardContent({\n children,\n className,\n ...props\n}: HTMLAttributes) {\n return (\n
\n {children}\n
\n );\n}\n\nexport function CardFooter({\n children,\n className,\n ...props\n}: HTMLAttributes) {\n return (\n
\n {children}\n
\n );\n}\n", "type": "registry:ui", "target": "components/crumble/ui/card.tsx" } ], "type": "registry:ui" }