design modal crop & handle crop image
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Alert, Snackbar } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
|
||||
function AlertMsg() {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
||||
const handleClick = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = (event, reason) => {
|
||||
if (reason === "clickaway") {
|
||||
return;
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Snackbar open={open} autoHideDuration={6000} onClose={handleClose}>
|
||||
<Alert
|
||||
onClose={handleClose}
|
||||
severity="success"
|
||||
variant="filled"
|
||||
sx={{ width: "100%" }}
|
||||
>
|
||||
This is a success Alert inside a Snackbar!
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
);
|
||||
}
|
||||
|
||||
export default AlertMsg;
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Button } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import Cropper from "react-easy-crop";
|
||||
import { cropImage } from "./cropImg";
|
||||
|
||||
function Crop({ data, uploadImg, handleClose }) {
|
||||
const [crop, setCrop] = useState({ x: 0, y: 0 });
|
||||
const [zoom, setZoom] = useState(1);
|
||||
const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
|
||||
|
||||
const onComplete = (imagePromisse) =>
|
||||
imagePromisse.then((image) => uploadImg(image));
|
||||
|
||||
return (
|
||||
<div className="mt-[16px]">
|
||||
<div className="px-[24px]">
|
||||
<div className="relative w-[424px] h-[380px] rounded-[8px] overflow-hidden">
|
||||
<Cropper
|
||||
crop={crop}
|
||||
zoom={zoom}
|
||||
aspect={4 / 3}
|
||||
image={data.blob}
|
||||
onCropChange={setCrop}
|
||||
onCropComplete={(_, croppedAreaPixels) =>
|
||||
setCroppedAreaPixels(croppedAreaPixels)
|
||||
}
|
||||
onZoomChange={setZoom}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-[16px] mt-[16px]">
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={handleClose}
|
||||
className="!text-[16px] !max-w-[calc(50%_-_8px)] dark:!bg-[#C7CEF4] dark:!text-[#5559CE] !font-medium !border-[#5559CE] !py-[5px] md:!py-[7px] lg:!py-[9px] !shadow-none"
|
||||
>
|
||||
انصراف
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => onComplete(cropImage(data.blob, croppedAreaPixels))}
|
||||
className="!text-[16px] !max-w-[calc(50%_-_8px)] !font-medium !shadow-none !py-[5px] md:!py-[7px] lg:!py-[9px]"
|
||||
>
|
||||
ذخیره
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Crop;
|
||||
@@ -0,0 +1,29 @@
|
||||
import CloseModalD from "@/components/icons/CloseModalD";
|
||||
import { styleDefault } from "@/mui";
|
||||
import { Box, Modal } from "@mui/material";
|
||||
import Crop from "./Crop";
|
||||
|
||||
function ModalUpload({ open, handleClose, data, uploadImg }) {
|
||||
return (
|
||||
<Modal open={open} onClose={handleClose} className="!hidden md:!flex">
|
||||
<Box
|
||||
sx={styleDefault}
|
||||
className="!pt-[12px] md:!pt-[14px] lg:!py-[16px]
|
||||
dark:!bg-[#222433] dark:!shadow-none !px-[12px] sm:!px-[16px] md:!px-[20px] lg:!px-[24px]
|
||||
!pb-[12px] sm:!pb-[16px] lg:!pb-[20px] dark:!border-transparent"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-[#3B3B3B] dark:text-[#DBDBDB] text-[14px] sm:text-[16px] md:text-[18px] lg:text-[20px] font-medium">
|
||||
برش عکس
|
||||
</p>
|
||||
<div className="cursor-pointer" onClick={handleClose}>
|
||||
<CloseModalD />
|
||||
</div>
|
||||
</div>
|
||||
<Crop data={data} uploadImg={uploadImg} handleClose={handleClose} />
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModalUpload;
|
||||
@@ -0,0 +1,47 @@
|
||||
const createImage = (url) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const image = new Image();
|
||||
image.addEventListener("load", () => resolve(image));
|
||||
image.addEventListener("error", (error) => reject(error));
|
||||
image.setAttribute("crossOrigin", "anonymous");
|
||||
image.src = url;
|
||||
});
|
||||
|
||||
async function getCroppedImg(imageSrc, pixelCrop) {
|
||||
const image = await createImage(imageSrc);
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
const maxSize = Math.max(image.width, image.height);
|
||||
const safeArea = 2 * ((maxSize / 2) * Math.sqrt(2));
|
||||
|
||||
canvas.width = safeArea;
|
||||
canvas.height = safeArea;
|
||||
|
||||
ctx.drawImage(
|
||||
image,
|
||||
safeArea / 2 - image.width * 0.5,
|
||||
safeArea / 2 - image.height * 0.5
|
||||
);
|
||||
const data = ctx.getImageData(0, 0, safeArea, safeArea);
|
||||
|
||||
canvas.width = pixelCrop.width;
|
||||
canvas.height = pixelCrop.height;
|
||||
|
||||
ctx.putImageData(
|
||||
data,
|
||||
Math.round(0 - safeArea / 2 + image.width * 0.5 - pixelCrop.x),
|
||||
Math.round(0 - safeArea / 2 + image.height * 0.5 - pixelCrop.y)
|
||||
);
|
||||
|
||||
return canvas.toDataURL("image/jpeg");
|
||||
}
|
||||
|
||||
export const cropImage = async (image, croppedAreaPixels, onError) => {
|
||||
try {
|
||||
const croppedImage = await getCroppedImg(image, croppedAreaPixels);
|
||||
return croppedImage;
|
||||
} catch (err) {
|
||||
onError(err);
|
||||
}
|
||||
};
|
||||
@@ -1,42 +1,58 @@
|
||||
"use client";
|
||||
|
||||
import { useRef } from "react";
|
||||
import { useRef, useState } from "react";
|
||||
import { Button } from "@mui/material";
|
||||
import ProfileP from "@/components/icons/ProfileP";
|
||||
import ArrowLeftPU from "@/components/icons/ArrowLeftPU";
|
||||
import { uploadImage } from "@/helper";
|
||||
import ModalUpload from "./ModalUpload";
|
||||
import Image from "next/image";
|
||||
|
||||
function UploadFile({ image, setImage }) {
|
||||
const fileRef = useRef();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [data, setData] = useState();
|
||||
|
||||
const handleClose = () => setOpen(false);
|
||||
const handleOpen = () => setOpen(true);
|
||||
|
||||
const openInput = () => fileRef.current.click();
|
||||
const handleChange = async (event) => {
|
||||
const result = await uploadImage(event);
|
||||
setImage(result);
|
||||
handleOpen();
|
||||
setData(result);
|
||||
};
|
||||
|
||||
const uploadImg = (prop) => {
|
||||
setImage(prop);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalUpload
|
||||
open={open}
|
||||
data={data}
|
||||
uploadImg={uploadImg}
|
||||
handleClose={handleClose}
|
||||
/>
|
||||
<input
|
||||
className="contents"
|
||||
ref={fileRef}
|
||||
type="file"
|
||||
ref={fileRef}
|
||||
className="contents"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{image && image.blob ? (
|
||||
{image ? (
|
||||
<Image
|
||||
width={24}
|
||||
height={24}
|
||||
src={image.blob}
|
||||
src={image}
|
||||
alt="img-uploaded"
|
||||
className="w-[74px] h-[74px] rounded-full overflow-hidden object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="p-[24px] grid cursor-pointer place-items-center border border-solid border-[#EFEFEF] bg-[#EFEFEF] rounded-full"
|
||||
onClick={openInput}
|
||||
>
|
||||
<div className="p-[24px] grid place-items-center border border-solid border-[#EFEFEF] bg-[#EFEFEF] rounded-full">
|
||||
<ProfileP />
|
||||
</div>
|
||||
)}
|
||||
@@ -54,7 +54,6 @@ function SearchBar() {
|
||||
<SearchD />
|
||||
</div>
|
||||
</Button>
|
||||
{/* <AlertMsg /> */}
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import UploadFile from "@/app/component/UploadFile";
|
||||
import UploadFile from "@/app/component/uploadFile";
|
||||
import { useState } from "react";
|
||||
|
||||
function Head() {
|
||||
const [image, setImage] = useState({
|
||||
file: "",
|
||||
blob: "",
|
||||
});
|
||||
const [image, setImage] = useState("");
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center md:justify-start gap-[10px]">
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import UploadFile from "@/app/component/UploadFile";
|
||||
import UploadFile from "@/app/component/uploadFile";
|
||||
import { useState } from "react";
|
||||
|
||||
function Head() {
|
||||
const [image, setImage] = useState({
|
||||
file: "",
|
||||
blob: "",
|
||||
});
|
||||
const [image, setImage] = useState("");
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center md:justify-start gap-[10px]">
|
||||
|
||||
@@ -4,6 +4,7 @@ export const styleDefault = {
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: "fit-content",
|
||||
outline: "none",
|
||||
bgcolor: "#FFF",
|
||||
borderRadius: 2,
|
||||
p: 2,
|
||||
|
||||
Generated
+33
@@ -28,7 +28,9 @@
|
||||
"npm": "^10.8.2",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18",
|
||||
"react-easy-crop": "^5.1.0",
|
||||
"react-google-map-picker": "^1.2.3",
|
||||
"react-images-uploading": "^3.1.7",
|
||||
"styled-components": "^6.1.11",
|
||||
"stylis": "^4.3.4",
|
||||
"stylis-plugin-rtl": "^2.1.1",
|
||||
@@ -14694,6 +14696,11 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-wheel": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz",
|
||||
"integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA=="
|
||||
},
|
||||
"node_modules/npm": {
|
||||
"version": "10.8.2",
|
||||
"resolved": "https://registry.npmjs.org/npm/-/npm-10.8.2.tgz",
|
||||
@@ -19346,6 +19353,19 @@
|
||||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-easy-crop": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-easy-crop/-/react-easy-crop-5.1.0.tgz",
|
||||
"integrity": "sha512-UsYeF/N7zoqtfOSD+2xSt1nRaoBYCI2YLkzmq+hi+aVepS4/bAMhbrLwJtDAP60jsVzWRiQCX7JG+ZtfWcHsiw==",
|
||||
"dependencies": {
|
||||
"normalize-wheel": "^1.0.1",
|
||||
"tslib": "^2.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.4.0",
|
||||
"react-dom": ">=16.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-error-boundary": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
|
||||
@@ -19377,6 +19397,19 @@
|
||||
"react": "^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-images-uploading": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/react-images-uploading/-/react-images-uploading-3.1.7.tgz",
|
||||
"integrity": "sha512-woET50eCezm645iIeP4gCoN7HjdR3T64UXC5l53yd+2vHFp+pwABH8Z/aAO5IXDeC1aP6doQ+K738L701zswAw==",
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
"npm": "^10.8.2",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18",
|
||||
"react-easy-crop": "^5.1.0",
|
||||
"react-google-map-picker": "^1.2.3",
|
||||
"react-images-uploading": "^3.1.7",
|
||||
"styled-components": "^6.1.11",
|
||||
"stylis": "^4.3.4",
|
||||
"stylis-plugin-rtl": "^2.1.1",
|
||||
|
||||
Reference in New Issue
Block a user