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";
|
"use client";
|
||||||
|
|
||||||
import { useRef } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { Button } from "@mui/material";
|
import { Button } from "@mui/material";
|
||||||
import ProfileP from "@/components/icons/ProfileP";
|
import ProfileP from "@/components/icons/ProfileP";
|
||||||
import ArrowLeftPU from "@/components/icons/ArrowLeftPU";
|
import ArrowLeftPU from "@/components/icons/ArrowLeftPU";
|
||||||
import { uploadImage } from "@/helper";
|
import { uploadImage } from "@/helper";
|
||||||
|
import ModalUpload from "./ModalUpload";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
function UploadFile({ image, setImage }) {
|
function UploadFile({ image, setImage }) {
|
||||||
const fileRef = useRef();
|
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 openInput = () => fileRef.current.click();
|
||||||
const handleChange = async (event) => {
|
const handleChange = async (event) => {
|
||||||
const result = await uploadImage(event);
|
const result = await uploadImage(event);
|
||||||
setImage(result);
|
handleOpen();
|
||||||
|
setData(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadImg = (prop) => {
|
||||||
|
setImage(prop);
|
||||||
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<ModalUpload
|
||||||
|
open={open}
|
||||||
|
data={data}
|
||||||
|
uploadImg={uploadImg}
|
||||||
|
handleClose={handleClose}
|
||||||
|
/>
|
||||||
<input
|
<input
|
||||||
className="contents"
|
|
||||||
ref={fileRef}
|
|
||||||
type="file"
|
type="file"
|
||||||
|
ref={fileRef}
|
||||||
|
className="contents"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
{image && image.blob ? (
|
{image ? (
|
||||||
<Image
|
<Image
|
||||||
width={24}
|
width={24}
|
||||||
height={24}
|
height={24}
|
||||||
src={image.blob}
|
src={image}
|
||||||
alt="img-uploaded"
|
alt="img-uploaded"
|
||||||
className="w-[74px] h-[74px] rounded-full overflow-hidden object-cover"
|
className="w-[74px] h-[74px] rounded-full overflow-hidden object-cover"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div className="p-[24px] grid place-items-center border border-solid border-[#EFEFEF] bg-[#EFEFEF] rounded-full">
|
||||||
className="p-[24px] grid cursor-pointer place-items-center border border-solid border-[#EFEFEF] bg-[#EFEFEF] rounded-full"
|
|
||||||
onClick={openInput}
|
|
||||||
>
|
|
||||||
<ProfileP />
|
<ProfileP />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -54,7 +54,6 @@ function SearchBar() {
|
|||||||
<SearchD />
|
<SearchD />
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
{/* <AlertMsg /> */}
|
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import UploadFile from "@/app/component/UploadFile";
|
import UploadFile from "@/app/component/uploadFile";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
function Head() {
|
function Head() {
|
||||||
const [image, setImage] = useState({
|
const [image, setImage] = useState("");
|
||||||
file: "",
|
|
||||||
blob: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center md:justify-start gap-[10px]">
|
<div className="flex items-center justify-center md:justify-start gap-[10px]">
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import UploadFile from "@/app/component/UploadFile";
|
import UploadFile from "@/app/component/uploadFile";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
function Head() {
|
function Head() {
|
||||||
const [image, setImage] = useState({
|
const [image, setImage] = useState("");
|
||||||
file: "",
|
|
||||||
blob: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center md:justify-start gap-[10px]">
|
<div className="flex items-center justify-center md:justify-start gap-[10px]">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export const styleDefault = {
|
|||||||
left: "50%",
|
left: "50%",
|
||||||
transform: "translate(-50%, -50%)",
|
transform: "translate(-50%, -50%)",
|
||||||
width: "fit-content",
|
width: "fit-content",
|
||||||
|
outline: "none",
|
||||||
bgcolor: "#FFF",
|
bgcolor: "#FFF",
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
p: 2,
|
p: 2,
|
||||||
|
|||||||
Generated
+33
@@ -28,7 +28,9 @@
|
|||||||
"npm": "^10.8.2",
|
"npm": "^10.8.2",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
|
"react-easy-crop": "^5.1.0",
|
||||||
"react-google-map-picker": "^1.2.3",
|
"react-google-map-picker": "^1.2.3",
|
||||||
|
"react-images-uploading": "^3.1.7",
|
||||||
"styled-components": "^6.1.11",
|
"styled-components": "^6.1.11",
|
||||||
"stylis": "^4.3.4",
|
"stylis": "^4.3.4",
|
||||||
"stylis-plugin-rtl": "^2.1.1",
|
"stylis-plugin-rtl": "^2.1.1",
|
||||||
@@ -14694,6 +14696,11 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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": {
|
"node_modules/npm": {
|
||||||
"version": "10.8.2",
|
"version": "10.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/npm/-/npm-10.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/npm/-/npm-10.8.2.tgz",
|
||||||
@@ -19346,6 +19353,19 @@
|
|||||||
"react": "^18.3.1"
|
"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": {
|
"node_modules/react-error-boundary": {
|
||||||
"version": "3.1.4",
|
"version": "3.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
|
||||||
@@ -19377,6 +19397,19 @@
|
|||||||
"react": "^17.0.0"
|
"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": {
|
"node_modules/react-is": {
|
||||||
"version": "18.3.1",
|
"version": "18.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||||
|
|||||||
@@ -29,7 +29,9 @@
|
|||||||
"npm": "^10.8.2",
|
"npm": "^10.8.2",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
|
"react-easy-crop": "^5.1.0",
|
||||||
"react-google-map-picker": "^1.2.3",
|
"react-google-map-picker": "^1.2.3",
|
||||||
|
"react-images-uploading": "^3.1.7",
|
||||||
"styled-components": "^6.1.11",
|
"styled-components": "^6.1.11",
|
||||||
"stylis": "^4.3.4",
|
"stylis": "^4.3.4",
|
||||||
"stylis-plugin-rtl": "^2.1.1",
|
"stylis-plugin-rtl": "^2.1.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user