UNPKG

@sendbird/uikit-react

Version:

Sendbird UIKit for React: A feature-rich and customizable chat UI kit with messaging, channel management, and user authentication.

161 lines (157 loc) 6.85 kB
import { e as __spreadArray } from './bundle-Bpofr334.js'; import { useState, useRef, useCallback, useEffect, useMemo } from 'react'; import { G as isImage } from './bundle-BZSLsKkw.js'; import { u as uuidv4 } from './bundle-LLA95Pqf.js'; import { b as validateFileTypes, v as validateFileCount, a as validateFileSizes } from './bundle-B5xkbY4c.js'; var buildPendingFile = function (file) { var fileIsImage = isImage(file.type); return { id: uuidv4(), file: file, previewUrl: fileIsImage ? URL.createObjectURL(file) : undefined, isImage: fileIsImage, }; }; /** * Holds files staged for the message composer before send. Producers (file * picker, drag-and-drop, clipboard paste) all call addFiles. The owning * wrapper drains pendingFiles on submit and calls clear(). */ var usePendingFiles = function (_a) { var uikitUploadSizeLimit = _a.uikitUploadSizeLimit, uikitMultipleFilesMessageLimit = _a.uikitMultipleFilesMessageLimit, acceptableMimeTypes = _a.acceptableMimeTypes, openModal = _a.openModal, stringSet = _a.stringSet, logger = _a.logger; var _b = useState([]), pendingFiles = _b[0], setPendingFiles = _b[1]; var pendingFilesRef = useRef(pendingFiles); pendingFilesRef.current = pendingFiles; var revokeUrl = useCallback(function (entry) { if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl); }, []); var addFiles = useCallback(function (files) { if (files.length === 0) return; if (!validateFileTypes({ files: files, acceptableMimeTypes: acceptableMimeTypes, openModal: openModal, stringSet: stringSet, logger: logger, logTag: 'usePendingFiles', })) return; var totalCount = pendingFilesRef.current.length + files.length; if (!validateFileCount({ totalCount: totalCount, uikitMultipleFilesMessageLimit: uikitMultipleFilesMessageLimit, openModal: openModal, stringSet: stringSet, logger: logger, logTag: 'usePendingFiles', })) return; if (!validateFileSizes({ files: files, uikitUploadSizeLimit: uikitUploadSizeLimit, openModal: openModal, stringSet: stringSet, logger: logger, logTag: 'usePendingFiles', })) return; setPendingFiles(function (current) { return __spreadArray(__spreadArray([], current, true), files.map(buildPendingFile), true); }); }, [uikitUploadSizeLimit, uikitMultipleFilesMessageLimit, acceptableMimeTypes, openModal, stringSet, logger]); var removeFile = useCallback(function (id) { setPendingFiles(function (current) { var target = current.find(function (entry) { return entry.id === id; }); if (target) revokeUrl(target); return current.filter(function (entry) { return entry.id !== id; }); }); }, [revokeUrl]); var clear = useCallback(function () { pendingFilesRef.current.forEach(revokeUrl); setPendingFiles([]); }, [revokeUrl]); useEffect(function () { return function () { pendingFilesRef.current.forEach(function (entry) { if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl); }); }; }, []); var hasPendingFiles = useMemo(function () { return pendingFiles.length > 0; }, [pendingFiles]); return { pendingFiles: pendingFiles, addFiles: addFiles, removeFile: removeFile, clear: clear, hasPendingFiles: hasPendingFiles, }; }; var dragHasFiles = function (event) { var _a, _b; // dataTransfer.types is the only reliable cross-browser signal during dragover; // dataTransfer.files is empty until drop in most browsers. return Array.from((_b = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.types) !== null && _b !== void 0 ? _b : []).includes('Files'); }; /** * Window-level drag-and-drop for file uploads. While enabled, the entire * viewport accepts file drops — the caller does not need to render a visual * affordance. * * When disabled, no listeners are attached, so the browser's default drop * behavior (open the file) is preserved. * * When multiple instances are mounted concurrently (e.g. main channel composer * + thread composer on desktop), each one passes a `shouldAccept` predicate so * exactly one consumes any given drop based on its position in the DOM. */ var useDragAndDrop = function (_a) { var onAddFiles = _a.onAddFiles, _b = _a.disabled, disabled = _b === void 0 ? false : _b, shouldAccept = _a.shouldAccept; // Latch callbacks in refs so listener registration only churns on disabled // flips. Otherwise inline shouldAccept / onAddFiles passed from a caller // would change identity each render and tear the listeners down + up, // risking missed drag events in between. var onAddFilesRef = useRef(onAddFiles); var shouldAcceptRef = useRef(shouldAccept); useEffect(function () { onAddFilesRef.current = onAddFiles; shouldAcceptRef.current = shouldAccept; }); useEffect(function () { if (disabled) return undefined; var onDragOver = function (event) { if (!dragHasFiles(event)) return; var shouldAcceptFn = shouldAcceptRef.current; if (shouldAcceptFn && !shouldAcceptFn(event)) return; // Required to mark the document as a valid drop target and suppress // the browser's default "open file" behavior on drop. event.preventDefault(); if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy'; }; var onDrop = function (event) { var _a, _b; if (!dragHasFiles(event)) return; var shouldAcceptFn = shouldAcceptRef.current; if (shouldAcceptFn && !shouldAcceptFn(event)) return; event.preventDefault(); var files = Array.from((_b = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.files) !== null && _b !== void 0 ? _b : []); if (files.length > 0) onAddFilesRef.current(files); }; window.addEventListener('dragover', onDragOver); window.addEventListener('drop', onDrop); return function () { window.removeEventListener('dragover', onDragOver); window.removeEventListener('drop', onDrop); }; }, [disabled]); }; export { usePendingFiles as a, useDragAndDrop as u }; //# sourceMappingURL=bundle-ExNQo0Ly.js.map