ar-design
Version:
AR Design is a (react | nextjs) ui library.
158 lines (157 loc) • 8.06 kB
JavaScript
import React, { useEffect, useRef, useState } from "react";
import "../../../assets/css/components/form/upload/styles.css";
import Tooltip from "../../feedback/tooltip";
import { ARIcon } from "../../icons";
const Upload = ({ text, file, onChange, allowedTypes, maxSize, multiple }) => {
// refs
const _firstLoad = useRef(false);
const _input = useRef(null);
const _arUplaod = useRef(null);
// refs -> File Data
const _validationErrors = useRef([]);
// states
const [selectedFiles, setSelectedFiles] = useState([]);
const [selectedFile, setSelectedFile] = useState(undefined);
const [validationErrors, setValidationErrors] = useState([]);
// methods
const handleFileChange = (files) => {
const _files = Array.from(files ?? []);
if (multiple) {
setSelectedFiles((prev) => {
const previousFileNames = prev.map((f) => f.name);
const newFiles = _files.filter((f) => !previousFileNames.includes(f.name)) ?? [];
return [...prev, ...newFiles];
});
}
else {
setSelectedFile(_files[0]);
}
};
const handleFileRemove = (fileToRemove) => {
if (multiple) {
const dataTransfer = new DataTransfer();
setSelectedFiles((prev) => {
const newList = prev.filter((x) => x.name !== fileToRemove.name);
newList.forEach((file) => dataTransfer.items.add(file));
if (_input.current)
_input.current.files = dataTransfer.files;
return newList;
});
}
else {
setSelectedFile(undefined);
}
};
const handleValidationFile = (file) => {
const newErrors = [];
if (allowedTypes) {
if (!allowedTypes.includes(file.type)) {
newErrors.push({ [file.name]: "Geçersiz dosya türü." });
_validationErrors.current.push(file.name);
}
}
if (maxSize) {
const _maxSize = maxSize * 1024 * 1024; // MB
if (file.size > _maxSize) {
newErrors.push({ [file.name]: "Dosya boyutu çok büyük." });
_validationErrors.current.push(file.name);
}
}
setValidationErrors((prev) => [...prev, ...newErrors]);
};
function handleFileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
if (reader.result && typeof reader.result === "string") {
resolve(reader.result);
}
else {
reject(new Error("Failed to read the file"));
}
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
// useEffects
useEffect(() => {
(async () => {
const dataTransfer = new DataTransfer();
const fileFormData = new FormData();
setValidationErrors([]);
_validationErrors.current = [];
if (_input.current) {
if (multiple) {
// Seçilmiş olan dosyalar validasyona gönderiliyor.
selectedFiles.forEach((f) => handleValidationFile(f));
const inValidFiles = Array.from(new Set(_validationErrors.current));
// Input içerisine dosyalar aktarılıyor.
selectedFiles.forEach((f) => dataTransfer.items.add(f));
_input.current.files = dataTransfer.files;
// Geçerli olan dosyalar alındı...
const validFiles = [...selectedFiles.filter((x) => !inValidFiles.includes(x.name))];
validFiles.forEach((f) => fileFormData.append("file", f));
// Geçerli olan dosyalar base64'e dönüştürülüyor...
const base64Array = await Promise.all(validFiles.map((validFile) => handleFileToBase64(validFile)));
onChange(fileFormData, validFiles, base64Array, _validationErrors.current.length === 0);
}
else {
if (selectedFile) {
handleValidationFile(selectedFile);
fileFormData.append("file", selectedFile);
onChange(fileFormData, selectedFile, await handleFileToBase64(selectedFile));
// Input içerisine dosyalar aktarılıyor.
dataTransfer.items.add(selectedFile);
_input.current.files = dataTransfer.files;
}
}
}
})();
}, [selectedFiles, selectedFile]);
useEffect(() => {
if (_firstLoad.current)
return;
multiple ? setSelectedFiles(file) : setSelectedFile(file);
_firstLoad.current = true;
}, [file]);
return (React.createElement("div", { ref: _arUplaod, className: "ar-upload" },
React.createElement("input", { ref: _input, type: "file", onChange: (event) => handleFileChange(event.target.files), multiple: multiple }),
React.createElement("div", { className: "ar-upload-button" },
React.createElement("div", { className: "button", onClick: () => {
if (_input.current)
_input.current.click();
} },
React.createElement("div", { className: "information" },
React.createElement(ARIcon, { variant: "linear", icon: "Upload", stroke: "var(--gray-600)", fill: "transparent" }),
React.createElement("div", { className: "properies" },
allowedTypes && (React.createElement("div", { className: "allow-types" }, allowedTypes?.map((allowedType) => allowedType.split("/")[1].toLocaleUpperCase()).join(", "))),
maxSize && React.createElement("div", { className: "max-size" },
"up to ",
maxSize,
"MB"))),
text && React.createElement("span", null, text)),
React.createElement("div", { className: "ar-upload-files" }, multiple ? (React.createElement("ul", null, selectedFiles.map((selectedFile, index) => {
let _className = [];
const errorMessages = validationErrors
.filter((error) => Object.keys(error).includes(selectedFile.name))
.map((error) => error[selectedFile.name]);
if (errorMessages.length > 0)
_className.push("error");
const content = (React.createElement("div", { className: "content" },
React.createElement("span", { className: _className.map((c) => c).join(" ") }, selectedFile.name),
React.createElement("span", { onClick: (event) => {
event.stopPropagation();
handleFileRemove(selectedFile);
} }, "x")));
return (React.createElement("li", { key: index, className: _className.map((c) => c).join(" ") }, errorMessages.length === 0 ? (content) : (React.createElement(Tooltip, { text: errorMessages.map((message) => message ?? "") }, content))));
}))) : (selectedFile && (React.createElement("div", { className: "file" },
React.createElement("div", { className: "information" },
React.createElement("span", null, selectedFile.name),
React.createElement("span", null,
(selectedFile.size / 1024).toFixed(3),
"KB")),
React.createElement("div", { className: "delete", onClick: () => handleFileRemove(selectedFile) },
React.createElement(ARIcon, { icon: "CloseCircle", fill: "transparent", size: 20 })))))))));
};
export default Upload;