import "./style.pcss"; import type { FC, HTMLAttributes, RefObject, ReactElement, } from "react"; import { useEffect, isValidElement, useRef, cloneElement, Children, useImperativeHandle, useMemo, } from "react"; import type { IFilesUploader, TInputChangeEvent, IOnFilesChangeArg, IFilesUploaderRef, } from "./config/types"; import { FilesUploaderCtx } from "./context/index"; import { getConvertedFileTypes } from "./lib/index"; import { useFilesUploader } from "./lib/useFilesUploader"; import { FilesUploaderList } from "./ui/FilesUploaderList"; import { FilesUploaderText } from "./ui/FilesUploaderText"; import { FilesUploaderTip } from "./ui/FilesUploaderTip"; import type { IComponentBaseProps } from "../../types/components/index"; import { useCN } from "../../utils/react/useCN"; import { useListeners } from "../../utils/react/useListeners"; import { useSSGRender } from "../../utils/react/useSSGRender"; import type { IInputRef } from "../input"; import { Input } from "../input"; /** * Компонент загрузчика файлов * @returns {ReactElement} * */ const FilesUploader: FC = (props): ReactElement => { const { baseClass = "filesUploader", extraClasses = {}, extraAttrs = {}, fileTypes = [ "jpg", "jpeg", "png", "pdf", "doc", "docx", "txt" ], maxSizeType = "all", name = "files", isDisabled = false, maxSize = 5, onFilesChange = null, items = [], maxFiles = 1, isRequired = false, children = null, slotFilesList = null, slotFileItem = null, slotTip = null, slotText = null, id = null, form = null, onFileDelete = null, onFileRender = null, onMount = null, onUnmount = null, ref, } = props; const { getCN } = useCN(baseClass); const containerRef: RefObject = useRef(null); const inputRef: RefObject = useRef(null); const hiddenInputRef: RefObject = useRef(null); const isMultiple = maxFiles > 1; const { isLoading, isDragging, isError, handleDragStart, handleDragEnter, handleDragLeave, handleDragOver, handleDrop, filesMap, handleChangeFiles, handleFileItemDeleteClick, setFilesMap, } = useFilesUploader(items, isDisabled, fileTypes, maxSize, maxFiles, hiddenInputRef, inputRef, onFilesChange, onFileDelete, maxSizeType); useEffect(() => { if (typeof onMount === "function") { onMount(); } return () => { if (typeof onUnmount === "function") { onUnmount(); } }; }, []); const handleChange = (e: TInputChangeEvent) => { e.stopPropagation(); handleChangeFiles(e); }; const handleReset = (e: Event) => { e.preventDefault(); setFilesMap(new Map()); }; useListeners({ ref: hiddenInputRef, listeners: { "reset": handleReset, }, }); useImperativeHandle(ref, (): IFilesUploaderRef => ({ el: containerRef.current, ref: null, getData: () => null, utils: null, stateActions: { setFiles: setFilesMap, }, }), [ filesMap, setFilesMap ]); const computedStates = { isError, isDisabled, isDragging, isMultiple, isLoading, }; const context = useMemo(() => ({ filesMap, maxSize, maxFiles, getCN, fileTypes, isDisabled, isLoading, isError, isDragging, isMultiple, onDeleteClick: handleFileItemDeleteClick, onFileRender, hiddenInputRef, setFilesMap, slotFileItem, maxSizeType, }), [ filesMap, maxFiles, maxSize, fileTypes, isDisabled, isError, isDragging, isMultiple, onFileRender, handleFileItemDeleteClick, setFilesMap, slotFileItem, maxSizeType ]); const SSGComponent = useSSGRender("data-js-files-uploader", props); if (SSGComponent) { return SSGComponent; } return (
{(!!slotTip && isValidElement(slotTip)) ? cloneElement(slotTip, { ...(slotTip?.props ?? {}), ...computedStates, }) : } {!!filesMap.size && ((!!slotFilesList && isValidElement(slotFilesList)) ? cloneElement(slotFilesList, { ...(slotFilesList?.props ?? {}), filesMap, handleFileItemDeleteClick, ...computedStates, } as HTMLAttributes) : )} {!!children && Children.map(children, ((child, index) => { if (isValidElement(child)) { return cloneElement(child, { ...(child?.props ?? {}), filesMap, key: `${index}`, ...computedStates, } as HTMLAttributes); } return child; }))}
); }; export type { IFilesUploader, IFilesUploaderRef, IOnFilesChangeArg }; export { FilesUploader };