UNPKG

@carbon/react

Version:

React components for the Carbon Design System

154 lines (152 loc) 5.31 kB
/** * 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. */ import { usePrefix } from "../../internal/usePrefix.js"; import { Enter, Space } from "../../internal/keyboard/keys.js"; import { matches } from "../../internal/keyboard/match.js"; import { useId } from "../../internal/useId.js"; import { noopFn } from "../../internal/noopFn.js"; import { deprecate } from "../../prop-types/deprecate.js"; import { composeEventHandlers } from "../../tools/events.js"; import classNames from "classnames"; import { useRef, useState } from "react"; import PropTypes from "prop-types"; import { jsx, jsxs } from "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 = noopFn, onClick, pattern = ".[0-9a-z]+$", innerRef, ...rest }) { const prefix = usePrefix(); const inputRef = useRef(null); const generatedId = useId(); const { current: uid } = useRef(id || generatedId); const [isActive, setActive] = useState(false); const dropareaClasses = classNames(`${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__ */ 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__ */ jsx("button", { type: "button", className: dropareaClasses, ref: innerRef, onKeyDown: (evt) => { if (matches(evt, [Enter, Space])) { evt.preventDefault(); inputRef.current?.click(); } }, onClick: composeEventHandlers([onClick, handleClick]), ...rest, children: labelText }), /* @__PURE__ */ jsx("label", { htmlFor: uid, className: `${prefix}--visually-hidden`, children: labelText }), /* @__PURE__ */ 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: PropTypes.arrayOf(PropTypes.string), className: PropTypes.string, disabled: PropTypes.bool, id: PropTypes.string, labelText: PropTypes.string.isRequired, maxFileSize: PropTypes.number, multiple: PropTypes.bool, name: PropTypes.string, onAddFiles: PropTypes.func, onClick: PropTypes.func, pattern: PropTypes.string, role: deprecate(PropTypes.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: deprecate(PropTypes.number, "The `tabIndex` prop for `FileUploaderButton` has been deprecated since it now renders a button element by default.") }; //#endregion export { FileUploaderDropContainer as default };