@hitachivantara/uikit-react-core
Version:
Core React components for the NEXT Design System.
195 lines (194 loc) • 7.01 kB
JavaScript
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
import { useContext, useState, useRef } from "react";
import { useDefaultProps } from "@hitachivantara/uikit-react-utils";
import { HvLabelContainer } from "../../FormElement/LabelContainer.js";
import { useLabels } from "../../hooks/useLabels.js";
import { useUniqueId } from "../../hooks/useUniqueId.js";
import { HvIcon } from "../../icons.js";
import { uniqueId } from "../../utils/helpers.js";
import { setId } from "../../utils/setId.js";
import { convertUnits } from "../utils.js";
import { useClasses } from "./DropZone.styles.js";
import { staticClasses } from "./DropZone.styles.js";
import { HvFormElementContext } from "../../FormElement/context.js";
const DEFAULT_LABELS = {
/** Extensions of the accepted file types */
acceptedFiles: "",
/** Dropzone area label. @deprecated use `label` prop instead */
dropzone: "Label",
/** Size file warning label. */
sizeWarning: "Max. file size:",
/** Size file warning label. */
drag: "Drop files here or",
/** Size file warning label. */
selectFiles: "click to upload",
/** Theming sheet used to style components */
dropFiles: "Drop files here",
/** Message to display when file size is greater than allowed */
fileSizeError: "The file exceeds the maximum upload size",
/** Message to display when file type is greater than allowed */
fileTypeError: "File type not allowed for upload",
removeFileButtonLabel: "Remove File"
};
function validateAccept(file, acceptAttr) {
if (!file || !acceptAttr) return true;
const acceptEntries = acceptAttr.split(",");
const fileName = file.name || "";
const mimeType = (file.type || "").toLowerCase();
const baseMimeType = mimeType.replace(/\/.*$/, "");
return acceptEntries.some((type) => {
const validType = type.trim().toLowerCase();
if (validType.charAt(0) === ".") {
return fileName.toLowerCase().endsWith(validType);
}
if (validType.endsWith("/*")) {
return baseMimeType === validType.replace(/\/.*$/, "");
}
return mimeType === validType;
});
}
const HvDropZone = (props) => {
const {
id: idProp,
classes: classesProp,
label,
labels: labelsProp,
accept,
maxFileSize,
inputProps,
hideLabels,
multiple = true,
onFilesAdded
} = useDefaultProps("HvDropZone", props);
const id = useUniqueId(idProp);
const labels = useLabels(DEFAULT_LABELS, labelsProp);
const { classes, cx } = useClasses(classesProp);
const { disabled } = useContext(HvFormElementContext);
const [dragState, setDragState] = useState(false);
const inputRef = useRef(null);
const handleDragLeave = () => {
setDragState(false);
};
const handleDragEnter = (event) => {
if (disabled) return;
event.stopPropagation();
event.preventDefault();
setDragState(true);
};
const onChangeHandler = (filesList) => {
const filesToProcess = Object.values(filesList);
const newFiles = filesToProcess.map((file) => {
const newFile = new File([file], file.name, {
type: file.type,
lastModified: file.lastModified
});
newFile.id = uniqueId("uploaded-file-data-");
const isSizeAllowed = file.size <= maxFileSize;
const isFileAccepted = !accept || accept.includes(file.type?.split("/")[1]) || // TODO: remove in v6
validateAccept(file, accept);
if (!isFileAccepted) {
newFile.errorMessage = labels?.fileTypeError;
newFile.status = "fail";
} else if (!isSizeAllowed) {
newFile.errorMessage = labels?.fileSizeError;
newFile.status = "fail";
}
return newFile;
});
onFilesAdded?.(newFiles);
};
const description = /* @__PURE__ */ jsxs(Fragment, { children: [
Number.isInteger(maxFileSize) && `${labels.sizeWarning} ${convertUnits(maxFileSize)}`,
labels.acceptedFiles || accept && ` (${accept?.replaceAll(",", ", ")})`
] });
return /* @__PURE__ */ jsxs(Fragment, { children: [
!hideLabels && /* @__PURE__ */ jsx(
HvLabelContainer,
{
id,
label: label ?? labels.dropzone,
description,
inputId: setId(id, "input-file"),
labelId: setId(id, "input-file-label"),
descriptionId: setId(id, "description"),
classes: {
root: classes.dropZoneLabelsGroup,
label: classes.dropZoneLabel
}
}
),
/* @__PURE__ */ jsxs(
"div",
{
id: setId(id, "input-file-container"),
className: cx(classes.dropZoneContainer, {
[classes.dragAction]: dragState,
[classes.dropZoneContainerDisabled]: disabled
}),
children: [
/* @__PURE__ */ jsx(
"input",
{
id: setId(id, "input-file"),
className: classes.inputArea,
type: "file",
multiple,
disabled,
title: !disabled ? `${labels?.drag} ${labels?.selectFiles}` : "",
onClick: () => {
if (inputRef.current) {
inputRef.current.value = "";
}
},
onChange: () => {
if (!disabled && inputRef.current?.files) {
onChangeHandler(inputRef.current.files);
}
},
onDragEnter: handleDragEnter,
onDragOver: handleDragEnter,
onDragLeave: handleDragLeave,
onDropCapture: handleDragLeave,
onDrop: (event) => {
if (disabled) return;
const { files } = event.dataTransfer;
if (multiple === true || files.length === 1) {
event.stopPropagation();
event.preventDefault();
onChangeHandler(files);
}
},
ref: inputRef,
accept,
...inputProps
}
),
/* @__PURE__ */ jsx("div", { className: classes?.dropArea, children: dragState ? /* @__PURE__ */ jsx("div", { className: classes.dropZoneAreaLabels, children: /* @__PURE__ */ jsx("div", { className: classes.dragText, children: labels?.dropFiles }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(
HvIcon,
{
name: "Doc",
size: "md",
className: classes.dropZoneAreaIcon
}
),
/* @__PURE__ */ jsx("div", { className: classes.dropZoneAreaLabels, children: /* @__PURE__ */ jsxs("div", { className: classes.dragText, children: [
labels?.drag,
/* @__PURE__ */ jsx(
"span",
{
className: classes.selectFilesText,
children: ` ${labels?.selectFiles}`
}
)
] }) })
] }) })
]
}
)
] });
};
export {
HvDropZone,
staticClasses as dropZoneClasses
};