UNPKG

@vectara/vectara-ui

Version:

Vectara's design system, codified as a React and Sass component library

70 lines (69 loc) 3.97 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { BiUpload } from "react-icons/bi"; import { VuiScreenBlock } from "../screenBlock/ScreenBlock"; import { VuiFlexContainer } from "../flex/FlexContainer"; import { VuiFlexItem } from "../flex/FlexItem"; import { VuiIcon } from "../icon/Icon"; import { VuiText } from "../typography/Text"; const defaultMessage = (_jsxs(VuiFlexContainer, Object.assign({ direction: "column", alignItems: "center", justifyContent: "center" }, { children: [_jsx(VuiFlexItem, { children: _jsx(VuiIcon, Object.assign({ size: "xxxl", color: "empty" }, { children: _jsx(BiUpload, {}) })) }), _jsx(VuiFlexItem, { children: _jsx(VuiText, Object.assign({ align: "center", size: "l" }, { children: _jsx("p", { children: "Drop files to add to upload" }) })) })] }))); export const VuiFileDropTarget = ({ onFilesDropped, scopeRef, message = defaultMessage }) => { const [isDragging, setIsDragging] = useState(false); const dragCounterRef = useRef(0); useEffect(() => { var _a; const target = (_a = scopeRef === null || scopeRef === void 0 ? void 0 : scopeRef.current) !== null && _a !== void 0 ? _a : document; // Reset whenever the target changes (e.g. when scopeRef attaches or the parent // swaps between scoped and global modes) so a stale counter doesn't suppress events. dragCounterRef.current = 0; setIsDragging(false); const handleDragEnter = (e) => { var _a; e.preventDefault(); dragCounterRef.current += 1; // Only show the overlay for drags that carry files, not e.g. selected text. if (dragCounterRef.current === 1 && ((_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.items) && e.dataTransfer.items.length > 0) { setIsDragging(true); } }; const handleDragLeave = (e) => { e.preventDefault(); dragCounterRef.current -= 1; if (dragCounterRef.current === 0) { setIsDragging(false); } }; const handleDragOver = (e) => { e.preventDefault(); }; const handleDrop = (e) => { var _a; e.preventDefault(); setIsDragging(false); dragCounterRef.current = 0; const files = (_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.files; if (files && files.length > 0) { onFilesDropped(Array.from(files)); } }; target.addEventListener("dragenter", handleDragEnter); target.addEventListener("dragleave", handleDragLeave); target.addEventListener("dragover", handleDragOver); target.addEventListener("drop", handleDrop); return () => { target.removeEventListener("dragenter", handleDragEnter); target.removeEventListener("dragleave", handleDragLeave); target.removeEventListener("dragover", handleDragOver); target.removeEventListener("drop", handleDrop); }; }, [scopeRef, onFilesDropped]); if (!isDragging) return null; // Scoped mode: overlay covers only the parent element. if (scopeRef === null || scopeRef === void 0 ? void 0 : scopeRef.current) { return createPortal(_jsx("div", Object.assign({ className: "vuiFileDropTarget__scopedOverlay" }, { children: _jsx("div", Object.assign({ className: "vuiFileDropTarget__message" }, { children: message })) })), scopeRef.current); } // Global mode: full-screen overlay. return (_jsx(VuiScreenBlock, Object.assign({ color: "primary" }, { children: _jsx("div", Object.assign({ className: "vuiFileDropTarget__messageContainer" }, { children: _jsx("div", Object.assign({ className: "vuiFileDropTarget__message" }, { children: message })) })) }))); };