UNPKG

@kopexa/sight

Version:

Kopexa Sight Design System — React component library for GRC applications

341 lines (337 loc) 10.5 kB
"use client"; "use strict"; "use client"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/hooks/use-file-upload/index.ts var use_file_upload_exports = {}; __export(use_file_upload_exports, { useFileUpload: () => useFileUpload }); module.exports = __toCommonJS(use_file_upload_exports); var import_i18n2 = require("@kopexa/i18n"); var import_shared_utils = require("@kopexa/shared-utils"); var import_react = require("react"); // src/hooks/use-file-upload/messages.ts var import_i18n = require("@kopexa/i18n"); var messages = (0, import_i18n.defineMessages)({ file_exceeds_max: { id: "use_file_upload.file_exceeds_max", defaultMessage: `File "{name}" exceeds the maximum size of {max}.`, description: "Shown when a single file is larger than allowed" }, file_not_accepted: { id: "use_file_upload.file_not_accepted", defaultMessage: `File "{name}" is not an accepted file type.`, description: "Shown when file type/extension doesn't match accept" }, too_many_files: { id: "file_upload.too_many_files", defaultMessage: "You can only upload a maximum of {max} files.", description: "Shown when maxFiles would be exceeded" } }); // src/hooks/use-file-upload/index.ts var useFileUpload = (options = {}) => { const { maxFiles = Number.POSITIVE_INFINITY, maxSize = Number.POSITIVE_INFINITY, accept = "*", multiple = false, initialFiles = [], onFilesChange, onFilesAdded, onError } = options; const t = (0, import_i18n2.useSafeIntl)(); const [state, setState] = (0, import_react.useState)({ files: initialFiles.map((file) => ({ file, id: file.id, preview: file.url })), isDragging: false, errors: [] }); const inputRef = (0, import_react.useRef)(null); const validateFile = (0, import_react.useCallback)( (file) => { if (file instanceof File) { if (file.size > maxSize) { return t.formatMessage(messages.file_exceeds_max, { name: file.name, max: (0, import_shared_utils.formatBytes)(maxSize) }); } } else { if (file.size > maxSize) { return t.formatMessage(messages.file_exceeds_max, { name: file.name, max: (0, import_shared_utils.formatBytes)(maxSize) }); } } if (accept !== "*") { const acceptedTypes = accept.split(",").map((type) => type.trim()); const fileType = file instanceof File ? file.type || "" : file.type; const fileExtension = `.${file instanceof File ? file.name.split(".").pop() : file.name.split(".").pop()}`; const isAccepted = acceptedTypes.some((type) => { if (type.startsWith(".")) { return fileExtension.toLowerCase() === type.toLowerCase(); } if (type.endsWith("/*")) { const baseType = type.split("/")[0]; return fileType.startsWith(`${baseType}/`); } return fileType === type; }); if (!isAccepted) { return t.formatMessage(messages.file_not_accepted, { name: file instanceof File ? file.name : file.name }); } } return null; }, [accept, maxSize, t.formatMessage] ); const createPreview = (0, import_react.useCallback)( (file) => { if (file instanceof File) { return URL.createObjectURL(file); } return file.url; }, [] ); const generateUniqueId = (0, import_react.useCallback)((file) => { if (file instanceof File) { return `${file.name}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`; } return file.id; }, []); const clearFiles = (0, import_react.useCallback)(() => { setState((prev) => { for (const file of prev.files) { if (file.preview && file.file instanceof File && file.file.type.startsWith("image/")) { URL.revokeObjectURL(file.preview); } } if (inputRef.current) { inputRef.current.value = ""; } const newState = { ...prev, files: [], errors: [] }; onFilesChange == null ? void 0 : onFilesChange(newState.files); return newState; }); }, [onFilesChange]); const addFiles = (0, import_react.useCallback)( (newFiles) => { if (!newFiles || newFiles.length === 0) return; const newFilesArray = Array.from(newFiles); const errors = []; setState((prev) => ({ ...prev, errors: [] })); if (!multiple) { clearFiles(); } if (multiple && maxFiles !== Number.POSITIVE_INFINITY && state.files.length + newFilesArray.length > maxFiles) { errors.push( t.formatMessage(messages.too_many_files, { max: maxFiles }) ); onError == null ? void 0 : onError(errors); setState((prev) => ({ ...prev, errors })); return; } const validFiles = []; for (const file of newFilesArray) { if (multiple) { const isDuplicate = state.files.some( (existingFile) => existingFile.file.name === file.name && existingFile.file.size === file.size ); if (isDuplicate) { return; } } if (file.size > maxSize) { errors.push( multiple ? `Some files exceed the maximum size of ${(0, import_shared_utils.formatBytes)(maxSize)}.` : `File exceeds the maximum size of ${(0, import_shared_utils.formatBytes)(maxSize)}.` ); continue; } const error = validateFile(file); if (error) { errors.push(error); } else { validFiles.push({ file, id: generateUniqueId(file), preview: createPreview(file) }); } } if (validFiles.length > 0) { onFilesAdded == null ? void 0 : onFilesAdded(validFiles); setState((prev) => { const newFiles2 = !multiple ? validFiles : [...prev.files, ...validFiles]; onFilesChange == null ? void 0 : onFilesChange(newFiles2); return { ...prev, files: newFiles2, errors }; }); } else if (errors.length > 0) { onError == null ? void 0 : onError(errors); setState((prev) => ({ ...prev, errors })); } if (inputRef.current) { inputRef.current.value = ""; } }, [ onError, state.files, maxFiles, multiple, maxSize, validateFile, createPreview, generateUniqueId, clearFiles, onFilesChange, onFilesAdded, t.formatMessage ] ); const removeFile = (0, import_react.useCallback)( (id) => { setState((prev) => { const fileToRemove = prev.files.find((file) => file.id === id); if ((fileToRemove == null ? void 0 : fileToRemove.preview) && fileToRemove.file instanceof File && fileToRemove.file.type.startsWith("image/")) { URL.revokeObjectURL(fileToRemove.preview); } const newFiles = prev.files.filter((file) => file.id !== id); onFilesChange == null ? void 0 : onFilesChange(newFiles); return { ...prev, files: newFiles, errors: [] }; }); }, [onFilesChange] ); const clearErrors = (0, import_react.useCallback)(() => { setState((prev) => ({ ...prev, errors: [] })); }, []); const handleDragEnter = (0, import_react.useCallback)((e) => { e.preventDefault(); e.stopPropagation(); setState((prev) => ({ ...prev, isDragging: true })); }, []); const handleDragLeave = (0, import_react.useCallback)((e) => { e.preventDefault(); e.stopPropagation(); if (e.currentTarget.contains(e.relatedTarget)) { return; } setState((prev) => ({ ...prev, isDragging: false })); }, []); const handleDragOver = (0, import_react.useCallback)((e) => { e.preventDefault(); e.stopPropagation(); }, []); const handleDrop = (0, import_react.useCallback)( (e) => { var _a; e.preventDefault(); e.stopPropagation(); setState((prev) => ({ ...prev, isDragging: false })); if ((_a = inputRef.current) == null ? void 0 : _a.disabled) { return; } if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { if (!multiple) { const file = e.dataTransfer.files[0]; addFiles([file]); } else { addFiles(e.dataTransfer.files); } } }, [addFiles, multiple] ); const handleFileChange = (0, import_react.useCallback)( (e) => { if (e.target.files && e.target.files.length > 0) { addFiles(e.target.files); } }, [addFiles] ); const openFileDialog = (0, import_react.useCallback)(() => { if (inputRef.current) { inputRef.current.click(); } }, []); const getInputProps = (0, import_react.useCallback)( (props = {}) => { return { ...props, type: "file", onChange: handleFileChange, accept: props.accept || accept, multiple: props.multiple !== void 0 ? props.multiple : multiple, ref: inputRef }; }, [accept, multiple, handleFileChange] ); return [ state, { addFiles, removeFile, clearFiles, clearErrors, handleDragEnter, handleDragLeave, handleDragOver, handleDrop, handleFileChange, openFileDialog, getInputProps } ]; }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { useFileUpload });