better-auth/apps/web/modules/saas/settings/components/CropImageDialog.tsx
 Илья Шалонин 8538ab969a first
2025-10-29 13:43:43 +05:00

79 lines
1.5 KiB
TypeScript

"use client";
import { Button } from "@ui/components/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@ui/components/dialog";
import { useMemo, useRef } from "react";
import type { ReactCropperElement } from "react-cropper";
import Cropper from "react-cropper";
export function CropImageDialog({
image,
open,
onOpenChange,
onCrop,
}: {
image: File | null;
open: boolean;
onOpenChange: (open: boolean) => void;
onCrop: (croppedImage: Blob | null) => void;
}) {
const cropperRef = useRef<ReactCropperElement>(null);
const getCroppedImage = async () => {
const cropper = cropperRef.current?.cropper;
const imageBlob = await new Promise<Blob | null>((resolve) => {
cropper
?.getCroppedCanvas({
maxWidth: 256,
maxHeight: 256,
})
.toBlob(resolve);
});
return imageBlob;
};
const imageSrc = useMemo(
() => image && URL.createObjectURL(image),
[image],
);
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle />
</DialogHeader>
<div>
{imageSrc && (
<Cropper
src={imageSrc}
style={{ width: "100%" }}
initialAspectRatio={1}
aspectRatio={1}
guides={true}
ref={cropperRef}
/>
)}
</div>
<DialogFooter>
<Button
onClick={async () => {
onCrop(await getCroppedImage());
onOpenChange(false);
}}
>
Save
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}