Add crop and upload to cloudinary
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
"@redwoodjs/forms": "^0.19.2",
|
"@redwoodjs/forms": "^0.19.2",
|
||||||
"@redwoodjs/router": "^0.19.2",
|
"@redwoodjs/router": "^0.19.2",
|
||||||
"@redwoodjs/web": "^0.19.2",
|
"@redwoodjs/web": "^0.19.2",
|
||||||
|
"cloudinary-react": "^1.6.7",
|
||||||
"controlkit": "^0.1.9",
|
"controlkit": "^0.1.9",
|
||||||
"golden-layout": "^1.5.9",
|
"golden-layout": "^1.5.9",
|
||||||
"jquery": "^3.5.1",
|
"jquery": "^3.5.1",
|
||||||
@@ -28,6 +29,8 @@
|
|||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
"react-dom": "^16.13.1",
|
"react-dom": "^16.13.1",
|
||||||
|
"react-dropzone": "^11.2.1",
|
||||||
|
"react-image-crop": "^8.6.6",
|
||||||
"rich-markdown-editor": "^11.0.2",
|
"rich-markdown-editor": "^11.0.2",
|
||||||
"styled-components": "^5.2.0",
|
"styled-components": "^5.2.0",
|
||||||
"three": "^0.118.3"
|
"three": "^0.118.3"
|
||||||
|
|||||||
Submodule web/src/cascade updated: 62f961293d...e634591e27
105
web/src/components/PartForm/ImageUploader.js
Normal file
105
web/src/components/PartForm/ImageUploader.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import React, { useCallback, useState } from "react";
|
||||||
|
import { useDropzone } from "react-dropzone";
|
||||||
|
import Button from "@material-ui/core/Button";
|
||||||
|
import axios from 'axios'
|
||||||
|
import ReactCrop from 'react-image-crop'
|
||||||
|
import { Dialog } from '@material-ui/core'
|
||||||
|
import 'react-image-crop/dist/ReactCrop.css'
|
||||||
|
|
||||||
|
const CLOUDINARY_UPLOAD_PRESET = process.env.GATSBY_PROD_PRESET || "dev_preset";
|
||||||
|
const CLOUDINARY_UPLOAD_URL = "https://api.cloudinary.com/v1_1/irevdev/upload";
|
||||||
|
|
||||||
|
export default function ImageUploader({ onImageUpload }) {
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||||
|
const [file, setFile] = useState()
|
||||||
|
const [crop, setCrop] = useState({
|
||||||
|
aspect: 16 / 9,
|
||||||
|
unit: '%',
|
||||||
|
width: 100,
|
||||||
|
});
|
||||||
|
async function handleImageUpload() {
|
||||||
|
var image = new Image();
|
||||||
|
image.src = file
|
||||||
|
const croppedFile = await getCroppedImg(image, crop, 'avatar')
|
||||||
|
console.log(croppedFile)
|
||||||
|
const imageData = new FormData();
|
||||||
|
imageData.append('upload_preset', CLOUDINARY_UPLOAD_PRESET);
|
||||||
|
imageData.append('file', croppedFile);
|
||||||
|
let upload = axios.post(CLOUDINARY_UPLOAD_URL, imageData)
|
||||||
|
try {
|
||||||
|
const { data } = await upload
|
||||||
|
if (data && data.public_id !== "") {
|
||||||
|
onImageUpload({cloudinaryPublicId: data.public_id})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('ERROR', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Drag and Drop
|
||||||
|
const onDrop = useCallback(acceptedFiles => {
|
||||||
|
setIsModalOpen(true)
|
||||||
|
const fileReader = new FileReader()
|
||||||
|
fileReader.onload = () => {
|
||||||
|
setFile(fileReader.result)
|
||||||
|
}
|
||||||
|
fileReader.readAsDataURL(acceptedFiles[0])
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="w-full relative" {...getRootProps()}>
|
||||||
|
<input {...getInputProps()} />
|
||||||
|
{/* <Button className variant="outlined">Upload</Button> */}
|
||||||
|
<button className="absolute inset-0"></button>
|
||||||
|
<div className="mt-3 text-indigo-500 border-dashed border border-indigo-500 py-8 text-center rounded-lg w-full">
|
||||||
|
Drop files here ...
|
||||||
|
or <span className="group flex w-full items-center justify-center">
|
||||||
|
<span className="bg-indigo-500 shadow rounded text-gray-200 cursor-pointer p-2 hover:shadow-lg transform hover:-translate-y-1 transition-all duration-150">upload</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Dialog
|
||||||
|
open={isModalOpen}
|
||||||
|
onClose={() => setIsModalOpen(false)}
|
||||||
|
>
|
||||||
|
<div className="p-4">
|
||||||
|
<ReactCrop src={file} crop={crop} onChange={newCrop => setCrop(newCrop)} />
|
||||||
|
<Button onClick={handleImageUpload} variant="outlined">Upload</Button>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCroppedImg(image, crop, fileName) {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const scaleX = image.naturalWidth / image.width;
|
||||||
|
const scaleY = image.naturalHeight / image.height;
|
||||||
|
canvas.width = crop.width;
|
||||||
|
canvas.height = crop.height;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
ctx.drawImage(
|
||||||
|
image,
|
||||||
|
crop.x * scaleX,
|
||||||
|
crop.y * scaleY,
|
||||||
|
crop.width * scaleX,
|
||||||
|
crop.height * scaleY,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
crop.width,
|
||||||
|
crop.height,
|
||||||
|
);
|
||||||
|
|
||||||
|
// As Base64 string
|
||||||
|
// const base64Image = canvas.toDataURL('image/jpeg');
|
||||||
|
|
||||||
|
// As a blob
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
canvas.toBlob(blob => {
|
||||||
|
blob.name = fileName;
|
||||||
|
resolve(blob);
|
||||||
|
}, 'image/jpeg', 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -4,12 +4,13 @@ import {
|
|||||||
FieldError,
|
FieldError,
|
||||||
Label,
|
Label,
|
||||||
TextField,
|
TextField,
|
||||||
TextAreaField,
|
|
||||||
Submit,
|
Submit,
|
||||||
} from '@redwoodjs/forms'
|
} from '@redwoodjs/forms'
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { navigate, routes } from '@redwoodjs/router'
|
import { navigate, routes } from '@redwoodjs/router'
|
||||||
import { useFlash } from '@redwoodjs/web'
|
import { useFlash } from '@redwoodjs/web'
|
||||||
|
import ImageUploader from './ImageUploader.js'
|
||||||
|
|
||||||
|
|
||||||
import Editor from "rich-markdown-editor";
|
import Editor from "rich-markdown-editor";
|
||||||
|
|
||||||
@@ -34,6 +35,8 @@ const PartForm = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div className="max-w-7xl mx-auto mt-10">
|
<div className="max-w-7xl mx-auto mt-10">
|
||||||
<Form onSubmit={onSubmit} error={props.error}>
|
<Form onSubmit={onSubmit} error={props.error}>
|
||||||
|
<ImageUploader onImageUpload={(yo) => {console.log('yo', yo)}} />
|
||||||
|
|
||||||
<FormError
|
<FormError
|
||||||
error={props.error}
|
error={props.error}
|
||||||
wrapperClassName="rw-form-error-wrapper"
|
wrapperClassName="rw-form-error-wrapper"
|
||||||
|
|||||||
52
yarn.lock
52
yarn.lock
@@ -4524,6 +4524,11 @@ atob@^2.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||||
|
|
||||||
|
attr-accept@^2.2.1:
|
||||||
|
version "2.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b"
|
||||||
|
integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==
|
||||||
|
|
||||||
autoprefixer@9.8.6, autoprefixer@^9.4.5, autoprefixer@^9.7.2:
|
autoprefixer@9.8.6, autoprefixer@^9.4.5, autoprefixer@^9.7.2:
|
||||||
version "9.8.6"
|
version "9.8.6"
|
||||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f"
|
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f"
|
||||||
@@ -5842,7 +5847,20 @@ clone-deep@^4.0.1:
|
|||||||
kind-of "^6.0.2"
|
kind-of "^6.0.2"
|
||||||
shallow-clone "^3.0.0"
|
shallow-clone "^3.0.0"
|
||||||
|
|
||||||
clsx@^1.0.4:
|
cloudinary-core@^2.11.3:
|
||||||
|
version "2.11.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/cloudinary-core/-/cloudinary-core-2.11.3.tgz#1440f61c6280485094aac87021b7e10f746dc69e"
|
||||||
|
integrity sha512-ZRnpjSgvx+LbSf+aEz5NKzxDB4Z0436aY/0BSDa90kAHiwAyd84VyEi95I74SE80e15Ri9t5S2xtksTXpzk9Xw==
|
||||||
|
|
||||||
|
cloudinary-react@^1.6.7:
|
||||||
|
version "1.6.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/cloudinary-react/-/cloudinary-react-1.6.7.tgz#13a8c8660a36377edbc2482cdc7f061a12d60b19"
|
||||||
|
integrity sha512-u7Mx2CWEPG7Iy/3jn+JcvYu50uHwem4XokveNDqdnoeBJwit2B/ccfUtOUbpSfKAaEKLxXuPcQR5H8FEpIoavw==
|
||||||
|
dependencies:
|
||||||
|
cloudinary-core "^2.11.3"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
|
clsx@^1.0.4, clsx@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
||||||
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
||||||
@@ -6159,7 +6177,7 @@ core-js-pure@^3.0.0, core-js-pure@^3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
|
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
|
||||||
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
|
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
|
||||||
|
|
||||||
core-js@3.6.5, core-js@^3.0.1, core-js@^3.0.4, core-js@^3.2.1:
|
core-js@3.6.5, core-js@^3.0.1, core-js@^3.0.4, core-js@^3.2.1, core-js@^3.6.5:
|
||||||
version "3.6.5"
|
version "3.6.5"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a"
|
||||||
integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==
|
integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==
|
||||||
@@ -7907,6 +7925,13 @@ file-loader@^6.0.0:
|
|||||||
loader-utils "^2.0.0"
|
loader-utils "^2.0.0"
|
||||||
schema-utils "^2.6.5"
|
schema-utils "^2.6.5"
|
||||||
|
|
||||||
|
file-selector@^0.2.2:
|
||||||
|
version "0.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.2.2.tgz#76186ac94ea01a18262a1e9ee36a8815911bc0b4"
|
||||||
|
integrity sha512-tMZc0lkFzhOGlZUAkQ5iljPORvDX+nWEI+9C5nj9KT7Ax8bAUUtI/GYM8JFIjyKfKlQkJRC84D0UgxwDqRGvRQ==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.0.3"
|
||||||
|
|
||||||
file-system-cache@^1.0.5:
|
file-system-cache@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/file-system-cache/-/file-system-cache-1.0.5.tgz#84259b36a2bbb8d3d6eb1021d3132ffe64cfff4f"
|
resolved "https://registry.yarnpkg.com/file-system-cache/-/file-system-cache-1.0.5.tgz#84259b36a2bbb8d3d6eb1021d3132ffe64cfff4f"
|
||||||
@@ -13096,6 +13121,15 @@ react-draggable@^4.0.3:
|
|||||||
classnames "^2.2.5"
|
classnames "^2.2.5"
|
||||||
prop-types "^15.6.0"
|
prop-types "^15.6.0"
|
||||||
|
|
||||||
|
react-dropzone@^11.2.1:
|
||||||
|
version "11.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-11.2.1.tgz#7544439ed2e27d1e4ac8efff5c6290b758cc29f5"
|
||||||
|
integrity sha512-AVWKQKKd4M8vIYzRC7QvvyzsGMrz6UAtAYW2WvSlEmstHKXhHL3CAq9LUzALfzMcDd2mxmntSNcpxij0w7U4qA==
|
||||||
|
dependencies:
|
||||||
|
attr-accept "^2.2.1"
|
||||||
|
file-selector "^0.2.2"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
|
||||||
react-error-overlay@^6.0.1, react-error-overlay@^6.0.3:
|
react-error-overlay@^6.0.1, react-error-overlay@^6.0.3:
|
||||||
version "6.0.7"
|
version "6.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
|
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
|
||||||
@@ -13141,6 +13175,15 @@ react-hotkeys@2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
prop-types "^15.6.1"
|
prop-types "^15.6.1"
|
||||||
|
|
||||||
|
react-image-crop@^8.6.6:
|
||||||
|
version "8.6.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-image-crop/-/react-image-crop-8.6.6.tgz#326f85b4da63c2f39a610f8832abea5d551a338f"
|
||||||
|
integrity sha512-2iQHKp12dYb6Fu2iN/Mg19BeLMgrbdOuKkU9k0RH7CHX6ZZylOlhfCM3/RsECbKnjGJRtGpXniGF+/i9CGis1A==
|
||||||
|
dependencies:
|
||||||
|
clsx "^1.1.1"
|
||||||
|
core-js "^3.6.5"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
|
||||||
react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1:
|
react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
@@ -15204,6 +15247,11 @@ tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.2, tslib@^1
|
|||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
|
||||||
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
|
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
|
||||||
|
|
||||||
|
tslib@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c"
|
||||||
|
integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==
|
||||||
|
|
||||||
tslib@~2.0.1:
|
tslib@~2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e"
|
||||||
|
|||||||
Reference in New Issue
Block a user