UNPKG

@drivy/cobalt

Version:

Opinionated design system for Drivy's projects.

222 lines (221 loc) 9.51 kB
import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import classnames from "classnames"; import { useEffect, useRef, useState } from "react"; import useBreakpoint from "../../hooks/useBreakpoint.js"; import { BinIcon, ContextualWarningCircleFilledIcon, LoadingIcon, PlusIcon } from "../Icon/index.js"; import Modal from "../Modal/index.js"; import Popover from "../Popover/index.js"; import { validateFile } from "../utils/validateFile.js"; const ACCEPTED_MAX_SIZE_MB = 10; const ACCEPTED_PHOTOS_TYPES = [ "jpg", "jpeg", "png", "gif" ]; const ERROR_DISPLAY_TIME = 6000; const ACCEPTED_TYPES_LOCALE_STRING = ACCEPTED_PHOTOS_TYPES.join(", "); const preventEventDefaults = (e)=>{ e.preventDefault?.(); e.stopPropagation?.(); }; const isEnterOrSpaceKey = (event)=>"Enter" === event.key || " " === event.key; const PhotoDropzone = ({ className, description, deleteContent, errorContent, onDropped, onPhotoDelete, deleteContentMode = "popover", initialImageUrl = "" })=>{ const { isMobile } = useBreakpoint(); const fileInputRef = useRef(null); const deleteButtonRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isErrored, setIsErrored] = useState(false); const [imagePreviewUrl, setImagePreviewUrl] = useState(initialImageUrl); const [displayDeletion, setDisplayDeletion] = useState(false); const [showDeletePopover, setShowDeletePopover] = useState(false); const [isImageLoaded, setIsImageLoaded] = useState(false); const openDeletePopover = (event)=>{ event && preventEventDefaults(event); setShowDeletePopover(true); }; const closeDeletePopover = (event)=>{ event && preventEventDefaults(event); setShowDeletePopover(false); }; const onDropzoneMouseEnter = (event)=>{ event && preventEventDefaults(event); if (imagePreviewUrl) setDisplayDeletion(true); }; const onDropzoneMouseLeave = (event)=>{ event && preventEventDefaults(event); if (displayDeletion && !showDeletePopover) setDisplayDeletion(false); }; const onDropzoneClick = (event)=>{ event && preventEventDefaults(event); if (isMobile && showDeletePopover) return; if (isLoading) return; if (isErrored) setIsErrored(false); if (imagePreviewUrl) { if (showDeletePopover) closeDeletePopover(); return setDisplayDeletion(!displayDeletion); } fileInputRef.current?.click(); }; const onDropzoneKey = (event)=>{ if (showDeletePopover) return; if (isEnterOrSpaceKey(event)) onDropzoneClick(); }; const onDeleteKey = (event)=>{ if (isEnterOrSpaceKey(event)) openDeletePopover(event); }; const onDelete = ()=>{ if (fileInputRef.current) fileInputRef.current.value = ""; closeDeletePopover(); setImagePreviewUrl(""); onPhotoDelete?.(); }; const onDragEnter = (event)=>{ preventEventDefaults(event); if (isErrored) setIsErrored(false); imagePreviewUrl || setIsDragging(true); }; const onDragLeave = (event)=>{ preventEventDefaults(event); setIsDragging(false); }; const processFile = async (fileToProcess)=>{ setIsDragging(false); setIsErrored(false); setIsLoading(true); const isValidFile = await validateFile(fileToProcess, (fileToValidate, extension)=>{ const maxFileSize = 1000 * ACCEPTED_MAX_SIZE_MB * 1000; const acceptedFileTypes = ACCEPTED_PHOTOS_TYPES; if (fileToValidate.size > maxFileSize) return false; if (!extension || !acceptedFileTypes.includes(extension)) return false; return true; }); if (isValidFile) { const imageSrc = URL.createObjectURL(fileToProcess); setImagePreviewUrl(imageSrc); await onDropped(fileToProcess); setIsLoading(false); } else { setIsLoading(false); setIsErrored(true); setTimeout(()=>{ setIsErrored(false); }, ERROR_DISPLAY_TIME); } }; const onFileInputChanged = (event)=>{ if (event.target.files?.length) processFile(event.target.files[0]); }; const onDrop = (event)=>{ preventEventDefaults(event); if (event.dataTransfer?.files?.length && !imagePreviewUrl) processFile(event.dataTransfer.files[0]); }; useEffect(()=>{ if (imagePreviewUrl) setIsImageLoaded(false); }, [ imagePreviewUrl ]); const onDeleteButtonClick = showDeletePopover ? closeDeletePopover : openDeletePopover; return /*#__PURE__*/ jsxs("label", { className: classnames("cobalt-photo-dropzone", className, { "cobalt-photo-dropzone--filled": imagePreviewUrl, "cobalt-photo-dropzone--dragging": isDragging, "cobalt-photo-dropzone--loading": isLoading, "cobalt-photo-dropzone--errored": isErrored, "cobalt-photo-dropzone--imageVisible": isImageLoaded }), onMouseEnter: onDropzoneMouseEnter, onMouseLeave: onDropzoneMouseLeave, onDragEnter: onDragEnter, onDragLeave: onDragLeave, onClick: onDropzoneClick, onKeyUp: onDropzoneKey, onDrop: onDrop, onDragStart: preventEventDefaults, onDragEnd: preventEventDefaults, onDragOver: preventEventDefaults, children: [ isErrored && /*#__PURE__*/ jsxs("div", { className: "cobalt-photo-dropzone__description", children: [ /*#__PURE__*/ jsx(ContextualWarningCircleFilledIcon, { color: "error" }), errorContent(ACCEPTED_TYPES_LOCALE_STRING, ACCEPTED_MAX_SIZE_MB) ] }), isLoading && /*#__PURE__*/ jsx("div", { className: "cobalt-photo-dropzone__description", children: /*#__PURE__*/ jsx(LoadingIcon, {}) }), !isLoading && !isErrored && (imagePreviewUrl ? /*#__PURE__*/ jsxs(Fragment, { children: [ /*#__PURE__*/ jsx("img", { className: "cobalt-photo-dropzone__preview", src: imagePreviewUrl, alt: "Uploaded preview", onLoad: ()=>{ setIsImageLoaded(true); } }), /*#__PURE__*/ jsxs("div", { children: [ /*#__PURE__*/ jsx("button", { type: "button", className: classnames("cobalt-photo-dropzone__delete-button", { "cobalt-photo-dropzone__delete-button--triggered": displayDeletion }), ref: deleteButtonRef, onClick: onDeleteButtonClick, onKeyUp: onDeleteKey, children: /*#__PURE__*/ jsx(BinIcon, { color: "onSurface" }) }), "modal" === deleteContentMode && /*#__PURE__*/ jsx(Modal, { isOpen: showDeletePopover, "aria-label": "delete", bodySpacing: false, children: deleteContent(onDelete, closeDeletePopover) }), "popover" === deleteContentMode && /*#__PURE__*/ jsx(Popover, { targetRef: deleteButtonRef, isOpen: showDeletePopover, onOpenChange: setShowDeletePopover, placement: "left-start", offset: [ 0, 12 ], bodySpacing: false, arrow: true, children: deleteContent(onDelete, closeDeletePopover) }) ] }) ] }) : /*#__PURE__*/ jsxs("div", { className: "cobalt-photo-dropzone__description cobalt-photo-dropzone__description--strong", children: [ description && /*#__PURE__*/ jsx("div", { children: description }), /*#__PURE__*/ jsx(PlusIcon, {}) ] })), /*#__PURE__*/ jsx("input", { ref: fileInputRef, className: "cobalt-photo-dropzone__hidden-input", type: "file", onChange: onFileInputChanged, onClick: (event)=>event.stopPropagation(), accept: ACCEPTED_PHOTOS_TYPES.map((ext)=>`.${ext}`).join(","), multiple: false }) ] }); }; const components_PhotoDropzone = PhotoDropzone; export default components_PhotoDropzone; //# sourceMappingURL=index.js.map