Dropzone accepted files filter - javascript

I'm trying to create a file upload for STL files. The code below works as in that in the onDrop function the console.log shows an empty array for all other file types and the files if they are of type STL. So it does what it's supposed to do.
However the line
{isDragReject && 'File type not accepted, sorry!'}
always fires, even for stl files. Which certainly would confuse the user.
import React, { useCallback } from 'react';
import Dropzone, { useDropzone } from 'react-dropzone';
const FileDropzone = () => {
const maxSize = 100000000;
const onDrop = useCallback((acceptedFiles) => {
console.log(acceptedFiles);
}, []);
const {
isDragActive,
getRootProps,
getInputProps,
isDragReject,
acceptedFiles,
rejectedFiles,
} = useDropzone({
onDrop,
accept: '.stl',
minSize: 0,
maxSize,
});
const isFileTooLarge =
rejectedFiles &&
rejectedFiles.length > 0 &&
rejectedFiles[0].size > maxSize;
return (
<div className="container text-center mt-5">
<div {...getRootProps()}>
<input {...getInputProps()} />
{!isDragActive && 'Click here or drop a file to upload!'}
{isDragActive && !isDragReject && "Drop it like it's hot!"}
{isDragReject && 'File type not accepted, sorry!'}
{isFileTooLarge && (
<div className="text-danger mt-2">File is too large.</div>
)}
</div>
</div>
);
};
export default FileDropzone;

This is a bug, please see details here: https://github.com/react-dropzone/react-dropzone/issues/888
Solution: Downgrade to previous version of DropZone.

Related

Next/image doesn't load my src image when I use a map

I use next/image to load my images in my app. It works fine except for a carousel with multiple images.
When I do like this I have that error :
Error: Image is missing required "src" property. Make sure you pass "src" in props to the next/image component. Received: {}
The problem is not because I have an entity without any file
image.js
import { getStrapiMedia } from "../utils/medias"
import NextImage from "next/image"
const Image = (props) => {
if (!props.media) {
return <NextImage {...props} />
}
const { url, alternativeText } = props.media
const loader = ({ src }) => {
return getStrapiMedia(src)
}
return (
<NextImage
loader={loader}
layout="responsive"
objectFit="contain"
width={props.media.width}
height={props.media.height}
src={url}
alt={alternativeText || ""}
/>
)
}
export default Image
Carousel.js
import React, { useCallback } from "react"
import useEmblaCarousel from "embla-carousel-react"
import NextImage from "./Image"
export const EmblaCarousel = (product) => {
const [emblaRef, emblaApi] = useEmblaCarousel()
useEmblaCarousel.globalOptions = { loop: true }
const scrollPrev = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev()
}, [emblaApi])
const scrollNext = useCallback(() => {
if (emblaApi) emblaApi.scrollNext()
}, [emblaApi])
return (
<div className="embla" ref={emblaRef}>
<div className="embla__container">
{product.gallery.map((_gallery) => (
<div key={_gallery.id}>
<NextImage media={_gallery.image} className="embla__slide" />
</div>
))}
</div>
<button
className="hidden md:inline embla__prev mr-2"
onClick={scrollPrev}
>
Prev
</button>
<button
className="hidden md:inline embla__next ml-2"
onClick={scrollNext}
>
Next
</button>
</div>
)
}
export default EmblaCarousel
The issue is
if (!props.media) {
return <NextImage {...props} />
}
in your custom Image component. When the media prop is falsy like undefined or null, you're passing everything else to NextImage but that everything else doesn’t include src prop which is mandatory for next Image component. Also your url extraction is dependent on media prop to be truthy and have a property called url. Can be seen from the next line :-
const { url, alternativeText } = props.media
And you intend to pass this url to src as can be seen from your usage. Either you can return null when media is falsy or you can filter out those items in your list where media prop is falsy and then map on it.
Not sure if you ever found an answer for this but I was running into the same issue and noticed that when looping through the multiple images object from Strapi the object is slightly different than with single images.
To fix this issue I supplied it to the getStrapiMedia() function in the same way it expects single images i.e:
{aboutpage?.attributes.shareCta.images.data.slice(0, 4).map((image) => (
<div key={image.id} className="relative h-64 w-full">
<Image
layout="fill"
objectFit="cover"
placeholder="blur"
blurDataURL={blurDataUrl}
src={
getStrapiMedia({ data: image }) ||
"/images/placeholders/image-placeholder.png"
}
/>
</div>
));
}
Hope this helps and kind regards
Replace NextImage with Image
import { getStrapiMedia } from "../utils/medias"
import Image from "next/image"
const NextImage = (props) => {
if (!props.media) {
return <Image {...props} />
}
const { url, alternativeText } = props.media
const loader = ({ src }) => {
return getStrapiMedia(src)
}
return (
<Image
loader={loader}
layout="responsive"
objectFit="contain"
width={props.media.width}
height={props.media.height}
src={url}
alt={alternativeText || ""}
/>
)
}
export default NextImage
Carousel.js
import React, { useCallback } from "react"
import useEmblaCarousel from "embla-carousel-react"
import NextImage from "./Image"
export const EmblaCarousel = (product) => {
const [emblaRef, emblaApi] = useEmblaCarousel()
useEmblaCarousel.globalOptions = { loop: true }
const scrollPrev = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev()
}, [emblaApi])
const scrollNext = useCallback(() => {
if (emblaApi) emblaApi.scrollNext()
}, [emblaApi])
return (
<div className="embla" ref={emblaRef}>
<div className="embla__container">
{product.gallery.map((_gallery) => (
<div key={_gallery.id}>
<NextImage media={_gallery.image} className="embla__slide" />
</div>
))}
</div>
<button
className="hidden md:inline embla__prev mr-2"
onClick={scrollPrev}
>
Prev
</button>
<button
className="hidden md:inline embla__next ml-2"
onClick={scrollNext}
>
Next
</button>
</div>
)
}
export default EmblaCarousel

How to show image upload previews with React?

I am building an image upload form using Next.js/React.js, and I want the user to be able to assign a tag to each upload. I also want to show the image preview using 'URL.createObjectURL'. The uploading works fine, but on the upload page where I try to iterate through the list of images to show the preview and show an input box to assign the tag, none of this is showing. I cannot work out why.
The code:
import { useState } from "react";
import Head from 'next/head'
import Layout, { siteTitle } from '../../components/layout'
import Image from 'next/image'
export default function PrivatePage(props) {
const [images, setImages] = useState([])
const [imageURLS, setImageURLS] = useState([])
const [tag, setTag] = useState(null)
const uploadImageToClient = (event) => {
var imageList = images
var urlList = imageURLS
if (event.target.files && event.target.files[0]) {
imageList.push(event.target.files[0]);
urlList.push(URL.createObjectURL(event.target.files[0]))
setImages(imageList);
setImageURLS(urlList);
}
};
const uploadTagToClient = (event) => {
if (event.target.value) {
const i = event.target.value;
setTag(i);
}
};
const uploadToServer = async (event) => {
const body = new FormData()
images.map((file, index) => {
body.append(`file${index}`, file);
});
body.append("tag", tag)
const response = await fetch("/api/file", {
method: "POST",
body
});
};
return (
<Layout home>
<Head>
<title>{siteTitle}</title>
</Head>
<div className="container">
<div className="row">
<h4>Select Images</h4>
<div className="col">
<input type="file" className="btn btn-outline-success-inverse" onChange={uploadImageToClient} />
<input type="file" className="btn btn-outline-success-inverse" onChange={uploadImageToClient} />
</div>
<div className="col">
</div>
<button
className="btn btn-outline-success-inverse"
type="submit"
onClick={uploadToServer}
>
Send to server
</button>
{images.map((file, index) => {
return (
<div class="row ">
<lead>{file.name}</lead>
<input type="text" onChange={uploadTagToClient} />
<image src={imageURLS[index]}/>
</div>)
})}
</div>
</div>
</Layout>
);
}
To clarify, nothing inside images.map is showing when I select images.
The issue happens because you're mutating the arrays you have in state with imageList.push and urlList.push which React doesn't pick up. This means state doesn't actually get updated and a re-render doesn't occur.
To fix it, rather than mutating those state arrays you need to create new ones when updating them.
const uploadImageToClient = (event) => {
if (event.target.files && event.target.files[0]) {
setImages((imageList) => [...imageList, event.target.files[0]]);
setImageURLS((urlList) => [
...urlList,
URL.createObjectURL(event.target.files[0])
]);
}
};
Unrelated to the main issue, you have several minor issues inside the render part of your component, specifically inside images.map.
You need to set a key prop on the outer <div> element;
The <lead> element doesn't exist, and needs to be replaced with a valid element;
The <image> element also doesn't exist, you probably meant <img> or <Image> (from next/image).
You can handle image upload with multiple image preview with following code.
const handleFile = (e) => {
setMessage("");
let file = e.target.files;
for (let i = 0; i < file.length; i++) {
const fileType = file[i]['type'];
const validImageTypes = ['image/gif', 'image/jpeg', 'image/png'];
if (validImageTypes.includes(fileType)) {
setFile([...files,file[i]]);
} else {
setMessage("only images accepted");
}
}
};
Follow this snippet https://bbbootstrap.com/snippets/multiple-image-upload-preview-and-remove-92816546

Add signature image to pdf in React

I have rendered a PDF file in react. I want to add a signature image to it by choosing an image file from the system and place it wherever I place the cursor on a pdf file.
After adding the signature we have to download the pdf along with the signature image on it.
I have used the #react-pdf-viewer library.
I tried but I don't know how to exactly approach this problem.
import React, { useState } from "react";
// Import the main component
import { Viewer } from "#react-pdf-viewer/core"; // install this library
// Plugins
import { defaultLayoutPlugin } from "#react-pdf-viewer/default-layout"; // install this library
// Import the styles
import "#react-pdf-viewer/core/lib/styles/index.css";
import "#react-pdf-viewer/default-layout/lib/styles/index.css";
// Worker
import { Worker } from "#react-pdf-viewer/core"; // install this library
export const App = () => {
// Create new plugin instance
const defaultLayoutPluginInstance = defaultLayoutPlugin();
//for unchange event
const [pdfFile, setPdfFile] = useState(null);
const [pdfFileError, setPdfFileError] = useState("");
//for submit event
const [viewPdf, setViewPdf] = useState(null);
// onchange event
const fileType = ["application/pdf"];
const handlePdfFileChange = (e) => {
let selectedFile = e.target.files[0];
if (selectedFile) {
if (selectedFile && fileType.includes(selectedFile.type)) {
let reader = new FileReader();
reader.readAsDataURL(selectedFile);
reader.onloadend = (e) => {
setPdfFile(e.target.result);
setPdfFileError("");
};
} else {
setPdfFile(null);
setPdfFileError("Please select valid pdf file");
}
} else {
console.log("select your file");
}
};
// form submit
const handlePdfFileSubmit = (e) => {
e.preventDefault();
if (pdfFile !== null) {
setViewPdf(pdfFile);
} else {
setViewPdf(null);
}
};
return (
<div className="container">
<br></br>
<form className="form-group" onSubmit={handlePdfFileSubmit}>
<input
type="file"
className="form-control"
required
onChange={handlePdfFileChange}
/>
{pdfFileError && <div className="error-msg">{pdfFileError}</div>}
<br></br>
<button type="submit" classname="btn btn-success btn-lg">
UPLOAD
</button>
</form>
<br></br>
<h4>View PDF</h4>
<div className="pdf-container">
{/* show pdf */}
{/* show pdf conditionally (if we have one) */}
{viewPdf && (
<>
<Worker workerUrl="https://unpkg.com/pdfjs-dist#2.6.347/build/pdf.worker.min.js">
<Viewer
fileUrl={viewPdf}
plugins={[defaultLayoutPluginInstance]}
/>
</Worker>
</>
)}
{/* if we dont have pdf or viewPdf state is null */}
{!viewPdf && <>No pdf file selected</>}
</div>
</div>
);
};
export default App;
My code for rendering PDF

importing PDF using gatsby with react pdf, returns error

Trying to import PDF using react pdf in gatsby, but getting a no file found error
import React, { useState } from 'react';
import { Document, Page } from 'react-pdf';
import mypdf from "../images/mypdf.pdf";
export default function MyDocuments() {
const [ numPages, setNumPages ] = useState(null);
const [ pageNumber, setPageNumber ] = useState(1);
function onDocumentLoadSuccess({ numPages }) {
setNumPages(numPages);
setPageNumber(1);
}
function changePage(offset) {
setPageNumber((prevPageNumber) => prevPageNumber + offset);
}
function previousPage() {
changePage(-1);
}
function nextPage() {
changePage(1);
}
return (
<div className="container">
<Document file={mypdf} onLoadSuccess={onDocumentLoadSuccess} className="container">
<Page pageNumber={pageNumber} />
</Document>
<div className="container">
<p>
Page {pageNumber || (numPages ? 1 : '--')} of {numPages || '--'}
</p>
<button type="button" disabled={pageNumber <= 1} onClick={previousPage}>
Previous
</button>
<button type="button" disabled={pageNumber >= numPages} onClick={nextPage}>
Next
</button>
</div>
</div>
);
}
ERROR:
Failed to load PDF file.
Page 1 of --
Already tried importing the asset and doing "../images/mypdf.pdf" and I know the file is there, anyone know how to fix to display the PDF correctly?

Deleting object from state array

I am in the middle of implementing delete functionality to an image uploader. My error message is Cannot read property 'id' of undefined. I don't think I am assigning an id correctly and my approach is wrong to the task at hand.
Tried passing the object into the function and using JavaScripts delete API to delete the specific file. Not best practice to mutate the state directly rather to copy the state, modify then paste the new state.
Tried using the name as the indentifier but does not work. I am recommended to delete by an id or key.
import * as React from "react"
import Dropzone, { DropFilesEventHandler } from "react-dropzone"
import FaIcon from "components/FaIcon"
import { PrimaryButton } from "components/Buttons"
interface ImageFile extends File {
preview?: string
id: any
}
class ImageUpload extends React.Component {
state = {
files: []
}
onDrop: DropFilesEventHandler = (files: ImageFile[]) => {
files.map(file => Object.assign(file, {
preview: URL.createObjectURL(file)
}))
this.setState({ files: [...this.state.files, ...files] })
}
deleteImage = (file: ImageFile, index: number) => {
console.log(file, index)
// this.setState({
// files: this.state.files.filter(file => file.name !== file),
// });
}
render() {
const files = this.state.files.map((file: ImageFile, index: number) => (
console.log(file),
(
<div key={index}>
<div>
<img src={file.preview} />
<div onClick={() => this.deleteImage(file, index)}>
<FaIcon icon="plus" />
</div>
</div>
</div>
)
))
return (
<div>
<div>
<h2>Upload Images</h2>
</div>
<form>
<div>
{files}
<Dropzone onDrop={this.onDrop} accept="image/*">
<FaIcon icon="plus"/>
</Dropzone>
</div>
<div>
<label>Category</label>
<div>
<input
type="text"
placeholder={"please enter / select..."}
value={this.state.categoryInput}
onChange={(e) => this.categoryInputValue(e)}
/>
</div>
<PrimaryButton>Upload</PrimaryButton>
</div>
</form>
</div >
)
}
}
export default ImageUpload
I expect the file to be removed from the array. Actual result is nothing happens and error message appears.
You could remove the object with an index value
deleteImage = (file, index) => {
const myNewFiles = [...this.state.files]; // copy of Original State
myNewFiles.splice(index, 1);
this.setState({
files: myNewFiles
});
};
and i've also made a sandbox example
https://codesandbox.io/embed/0krqwkokw
deleteImage = (file_id) => {
const { files } = this.state;
this.setState({
files: files.filter(file => file.id !== file_id),
});
}
You should write this as you are passing file id into the deleteImage function.

Categories