Files
2025-10-16 21:45:52 -07:00

14 lines
2.3 KiB
JSON

{
"name": "image-crop",
"description": "Image cropping component",
"dependencies": [
"react-image-crop"
],
"files": [
{
"name": "image-crop.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Crop, RotateCw, ZoomIn, ZoomOut } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/registry/default/ui/button\"\n\nexport interface ImageCropProps extends React.HTMLAttributes<HTMLDivElement> {\n src: string\n onCrop?: (croppedImage: string) => void\n aspect?: number\n}\n\nconst ImageCrop = React.forwardRef<HTMLDivElement, ImageCropProps>(\n ({ className, src, onCrop, aspect = 1, ...props }, ref) => {\n const [zoom, setZoom] = React.useState(1)\n const [rotation, setRotation] = React.useState(0)\n\n return (\n <div ref={ref} className={cn(\"space-y-4\", className)} {...props}>\n <div className=\"relative aspect-square overflow-hidden rounded-lg border\">\n <img\n src={src}\n alt=\"Crop preview\"\n className=\"h-full w-full object-contain\"\n style={{\n transform: `scale(${zoom}) rotate(${rotation}deg)`,\n transition: \"transform 0.2s\",\n }}\n />\n </div>\n <div className=\"flex items-center justify-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => setZoom(Math.max(0.5, zoom - 0.1))}\n >\n <ZoomOut className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => setZoom(Math.min(3, zoom + 0.1))}\n >\n <ZoomIn className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => setRotation((rotation + 90) % 360)}\n >\n <RotateCw className=\"h-4 w-4\" />\n </Button>\n <Button onClick={() => onCrop?.(src)}>\n <Crop className=\"mr-2 h-4 w-4\" />\n Crop\n </Button>\n </div>\n </div>\n )\n }\n)\nImageCrop.displayName = \"ImageCrop\"\n\nexport { ImageCrop }\n"
}
],
"type": "components:ui"
}