@carbon/react
Version:
React components for the Carbon Design System
158 lines (156 loc) • 5.96 kB
JavaScript
/**
* Copyright IBM Corp. 2016, 2026
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
const require_runtime = require("../../_virtual/_rolldown/runtime.js");
const require_usePrefix = require("../../internal/usePrefix.js");
const require_keys = require("../../internal/keyboard/keys.js");
const require_match = require("../../internal/keyboard/match.js");
const require_useId = require("../../internal/useId.js");
const require_noopFn = require("../../internal/noopFn.js");
const require_deprecate = require("../../prop-types/deprecate.js");
const require_events = require("../../tools/events.js");
let classnames = require("classnames");
classnames = require_runtime.__toESM(classnames);
let react = require("react");
react = require_runtime.__toESM(react);
let prop_types = require("prop-types");
prop_types = require_runtime.__toESM(prop_types);
let react_jsx_runtime = require("react/jsx-runtime");
//#region src/components/FileUploader/FileUploaderDropContainer.tsx
/**
* Copyright IBM Corp. 2016, 2026
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
function FileUploaderDropContainer({ accept = [], className, id, disabled, labelText = "Add file", maxFileSize, multiple = false, name, onAddFiles = require_noopFn.noopFn, onClick, pattern = ".[0-9a-z]+$", innerRef, ...rest }) {
const prefix = require_usePrefix.usePrefix();
const inputRef = (0, react.useRef)(null);
const generatedId = require_useId.useId();
const { current: uid } = (0, react.useRef)(id || generatedId);
const [isActive, setActive] = (0, react.useState)(false);
const dropareaClasses = (0, classnames.default)(`${prefix}--file__drop-container`, `${prefix}--file-browse-btn`, {
[`${prefix}--file__drop-container--drag-over`]: isActive,
[`${prefix}--file-browse-btn--disabled`]: disabled
}, className);
/**
* Filters the array of added files based on file type and size restrictions
*/
function validateFiles(transferredFiles) {
const acceptedTypes = new Set(accept);
return transferredFiles.reduce((acc, curr) => {
const { name, type: mimeType = "" } = curr;
const fileExtensionRegExp = new RegExp(pattern, "i");
const [fileExtension] = name.match(fileExtensionRegExp) ?? [];
if (maxFileSize && curr.size > maxFileSize) {
curr.invalidFileType = true;
return acc.concat([curr]);
}
if (!accept.length) return acc.concat([curr]);
if (fileExtension === void 0) return acc;
if (acceptedTypes.has(mimeType) || acceptedTypes.has(fileExtension.toLowerCase())) return acc.concat([curr]);
curr.invalidFileType = true;
return acc.concat([curr]);
}, []);
}
const handleFiles = (event, files) => {
if (!files.length) return onAddFiles(event, { addedFiles: [] });
return onAddFiles(event, { addedFiles: validateFiles(multiple ? files : [files[0]]) });
};
const handleChange = (event) => {
return handleFiles(event, [...event.target.files ?? []]);
};
const handleDrop = (event) => {
const items = [...event.dataTransfer.items ?? []];
return handleFiles(event, items.length ? items.reduce((acc, item) => {
if (item.kind !== "file") return acc;
if (item.webkitGetAsEntry()?.isDirectory) return acc;
const file = item.getAsFile();
if (file) acc.push(file);
return acc;
}, []) : [...event.dataTransfer.files]);
};
const handleClick = () => {
if (!disabled) inputRef.current?.click();
};
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `${prefix}--file`,
onDragOver: (evt) => {
evt.stopPropagation();
evt.preventDefault();
if (disabled) return;
setActive(true);
evt.dataTransfer.dropEffect = "copy";
},
onDragLeave: (evt) => {
evt.stopPropagation();
evt.preventDefault();
if (disabled) return;
setActive(false);
evt.dataTransfer.dropEffect = "move";
},
onDrop: (evt) => {
evt.stopPropagation();
evt.preventDefault();
if (disabled) return;
setActive(false);
handleDrop(evt);
},
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
className: dropareaClasses,
ref: innerRef,
onKeyDown: (evt) => {
if (require_match.matches(evt, [require_keys.Enter, require_keys.Space])) {
evt.preventDefault();
inputRef.current?.click();
}
},
onClick: require_events.composeEventHandlers([onClick, handleClick]),
...rest,
children: labelText
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
htmlFor: uid,
className: `${prefix}--visually-hidden`,
children: labelText
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
type: "file",
id: uid,
className: `${prefix}--file-input`,
ref: inputRef,
tabIndex: -1,
disabled,
accept: accept.join(","),
name,
multiple,
onChange: handleChange,
onClick: (evt) => {
evt.target.value = "";
}
})
]
});
}
FileUploaderDropContainer.propTypes = {
accept: prop_types.default.arrayOf(prop_types.default.string),
className: prop_types.default.string,
disabled: prop_types.default.bool,
id: prop_types.default.string,
labelText: prop_types.default.string.isRequired,
maxFileSize: prop_types.default.number,
multiple: prop_types.default.bool,
name: prop_types.default.string,
onAddFiles: prop_types.default.func,
onClick: prop_types.default.func,
pattern: prop_types.default.string,
role: require_deprecate.deprecate(prop_types.default.number, "The `role` prop for `FileUploaderButton` has been deprecated since it now renders a button element by default, and has an implicit role of button."),
tabIndex: require_deprecate.deprecate(prop_types.default.number, "The `tabIndex` prop for `FileUploaderButton` has been deprecated since it now renders a button element by default.")
};
//#endregion
exports.default = FileUploaderDropContainer;