@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
JavaScript
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