analytica-frontend-lib
Version:
Repositório público dos componentes utilizados nas plataformas da Analytica Ensino
338 lines (329 loc) • 13.8 kB
JavaScript
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
var _chunk34ST3MKOjs = require('./chunk-34ST3MKO.js');
var _chunkANT66KVKjs = require('./chunk-ANT66KVK.js');
var _chunkTN3AYOMVjs = require('./chunk-TN3AYOMV.js');
// src/components/FileDropzone/FileDropzone.tsx
var _react = require('react');
var _Upload = require('@phosphor-icons/react/dist/csr/Upload');
var _WarningCircle = require('@phosphor-icons/react/dist/csr/WarningCircle');
var _CheckCircle = require('@phosphor-icons/react/dist/csr/CheckCircle');
var _FileVideo = require('@phosphor-icons/react/dist/csr/FileVideo');
var _FileAudio = require('@phosphor-icons/react/dist/csr/FileAudio');
var _FilePdf = require('@phosphor-icons/react/dist/csr/FilePdf');
var _ClosedCaptioning = require('@phosphor-icons/react/dist/csr/ClosedCaptioning');
var _Paperclip = require('@phosphor-icons/react/dist/csr/Paperclip');
var _PencilSimple = require('@phosphor-icons/react/dist/csr/PencilSimple');
var _X = require('@phosphor-icons/react/dist/csr/X');
var _jsxruntime = require('react/jsx-runtime');
var FILE_TYPE_CONFIG = {
image: {
icon: _Upload.UploadIcon,
defaultPlaceholder: "para subir a imagem ou arraste aqui"
},
video: {
icon: _FileVideo.FileVideoIcon,
defaultPlaceholder: "para subir o v\xEDdeo ou arraste aqui"
},
audio: {
icon: _FileAudio.FileAudioIcon,
defaultPlaceholder: "para subir o \xE1udio ou arraste aqui"
},
pdf: {
icon: _FilePdf.FilePdfIcon,
defaultPlaceholder: "para subir o PDF ou arraste aqui"
},
subtitle: {
icon: _ClosedCaptioning.ClosedCaptioningIcon,
defaultPlaceholder: "para subir a legenda ou arraste aqui"
}
};
function FileDropzone({
label,
helperText,
errorMessage,
fileUrl,
selectedFile,
onFileSelect,
onRemoveFile,
accept,
fileType,
actionText = "Clique aqui",
placeholder,
changeText,
changeButtonText = "Trocar",
disabled = false,
required = false,
className,
maxSize,
onSizeError,
onTypeError,
showPreview = true,
previewMaxHeight = "max-h-48"
}) {
const fileInputRef = _react.useRef.call(void 0, null);
const dragCounterRef = _react.useRef.call(void 0, 0);
const [isDragging, setIsDragging] = _react.useState.call(void 0, false);
const [previewUrl, setPreviewUrl] = _react.useState.call(void 0, null);
const generatedId = _react.useId.call(void 0, );
const inputId = `file-dropzone-${generatedId}`;
const hasError = Boolean(errorMessage);
const config = FILE_TYPE_CONFIG[fileType];
const IconComponent = config.icon;
const defaultPlaceholder = placeholder || config.defaultPlaceholder;
const defaultChangeText = changeText || (fileType === "image" ? "Clique para alterar a imagem" : "Clique para trocar o arquivo");
_react.useEffect.call(void 0, () => {
if (!selectedFile || fileType !== "image") {
setPreviewUrl(null);
return;
}
const url = URL.createObjectURL(selectedFile);
setPreviewUrl(url);
return () => {
URL.revokeObjectURL(url);
};
}, [selectedFile, fileType]);
const displayUrl = fileType === "image" ? previewUrl || fileUrl : null;
const hasFile = Boolean(selectedFile || fileUrl);
const validateFile = _react.useCallback.call(void 0,
(file) => {
const acceptedTypes = accept.split(",").map((type) => type.trim());
const isValidType = acceptedTypes.some((type) => {
if (type.startsWith(".")) {
return file.name.toLowerCase().endsWith(type.toLowerCase());
}
if (!file.type) {
return false;
}
if (type.endsWith("/*")) {
const mainType = type.split("/")[0];
return file.type.startsWith(mainType + "/");
}
return file.type === type;
});
if (!isValidType) {
_optionalChain([onTypeError, 'optionalCall', _ => _(file)]);
return false;
}
if (maxSize != null && file.size > maxSize) {
_optionalChain([onSizeError, 'optionalCall', _2 => _2(file, maxSize)]);
return false;
}
return true;
},
[accept, maxSize, onSizeError, onTypeError]
);
const handleFileChange = (event) => {
const file = _optionalChain([event, 'access', _3 => _3.target, 'access', _4 => _4.files, 'optionalAccess', _5 => _5[0]]);
if (!file || disabled) return;
if (validateFile(file)) {
_optionalChain([onFileSelect, 'optionalCall', _6 => _6(file)]);
}
event.target.value = "";
};
const handleDragEnter = (e) => {
e.preventDefault();
e.stopPropagation();
dragCounterRef.current += 1;
if (!disabled && dragCounterRef.current === 1) {
setIsDragging(true);
}
};
const handleDragLeave = (e) => {
e.preventDefault();
e.stopPropagation();
dragCounterRef.current -= 1;
if (dragCounterRef.current === 0) {
setIsDragging(false);
}
};
const handleDragOver = (e) => {
e.preventDefault();
e.stopPropagation();
};
const handleDrop = (e) => {
e.preventDefault();
e.stopPropagation();
dragCounterRef.current = 0;
setIsDragging(false);
if (disabled) return;
const file = _optionalChain([e, 'access', _7 => _7.dataTransfer, 'access', _8 => _8.files, 'optionalAccess', _9 => _9[0]]);
if (!file) return;
if (validateFile(file)) {
_optionalChain([onFileSelect, 'optionalCall', _10 => _10(file)]);
}
};
const handleRemove = (e) => {
e.preventDefault();
e.stopPropagation();
if (!disabled) {
_optionalChain([onRemoveFile, 'optionalCall', _11 => _11()]);
}
};
const handleChangeClick = (e) => {
e.preventDefault();
e.stopPropagation();
if (!disabled) {
_optionalChain([fileInputRef, 'access', _12 => _12.current, 'optionalAccess', _13 => _13.click, 'call', _14 => _14()]);
}
};
const fileName = _optionalChain([selectedFile, 'optionalAccess', _15 => _15.name]) || (fileUrl ? "Arquivo carregado" : "");
const getBorderClasses = () => {
if (hasError) {
return disabled ? "border-indicator-error" : "border-indicator-error hover:border-indicator-error";
}
if (isDragging) {
return "border-primary-500 bg-primary-50";
}
return disabled ? "border-border-200" : "border-border-200 hover:border-primary-500";
};
const dropzoneClasses = _chunkTN3AYOMVjs.cn.call(void 0,
"flex flex-col items-center justify-center p-6 border-2 border-dashed rounded-lg cursor-pointer transition-colors",
getBorderClasses(),
disabled && "cursor-not-allowed opacity-50"
);
const isImagePreview = fileType === "image" && showPreview && Boolean(displayUrl);
const imageCardClasses = _chunkTN3AYOMVjs.cn.call(void 0,
"flex flex-col items-center justify-center gap-3 p-6 rounded-lg bg-background-100",
hasError && "border-2 border-indicator-error",
disabled && "opacity-50"
);
const getAriaDescribedBy = () => {
if (errorMessage) return `${inputId}-error`;
if (helperText) return `${inputId}-helper`;
return void 0;
};
const renderImagePreview = () => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"img",
{
src: _nullishCoalesce(displayUrl, () => ( void 0)),
alt: "Preview do arquivo",
className: _chunkTN3AYOMVjs.cn.call(void 0, "max-w-full rounded-lg object-contain", previewMaxHeight)
}
),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2 rounded-full border border-border-200 bg-background px-3 py-1.5", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _Paperclip.PaperclipIcon, { size: 16, className: "text-text-600" }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "sm", className: "text-text-700 max-w-[200px] truncate", children: fileName }),
onRemoveFile && !disabled && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_chunk34ST3MKOjs.Button_default,
{
variant: "raw",
type: "button",
onClick: handleRemove,
className: "p-0.5 rounded-full hover:bg-error-100 transition-colors",
"aria-label": "Remover arquivo",
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _X.XIcon, { size: 14, className: "text-indicator-error" })
}
)
] }),
!disabled && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_chunk34ST3MKOjs.Button_default,
{
variant: "outline",
action: "primary",
size: "small",
iconLeft: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _PencilSimple.PencilSimpleIcon, { size: 16 }),
onClick: handleChangeClick,
children: changeButtonText
}
)
] });
const renderFilePreview = () => {
if (hasFile) {
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col items-center gap-3", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex items-center justify-center w-12 h-12 rounded-full bg-success-100", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_CheckCircle.CheckCircleIcon,
{
size: 28,
className: "text-success-600",
weight: "fill"
}
) }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, IconComponent, { size: 20, className: "text-text-600" }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "sm", className: "text-text-700 max-w-[200px] truncate", children: fileName }),
onRemoveFile && !disabled && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_chunk34ST3MKOjs.Button_default,
{
variant: "raw",
type: "button",
onClick: handleRemove,
className: "p-1 rounded-full hover:bg-error-100 transition-colors",
"aria-label": "Remover arquivo",
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _X.XIcon, { size: 14, className: "text-indicator-error" })
}
)
] }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { size: "xs", className: "text-text-500", children: defaultChangeText })
] });
}
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, IconComponent, { size: 24, className: "text-primary-500 mb-2" }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkANT66KVKjs.Text_default, { size: "sm", className: "text-text-600 text-center", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-primary-500 font-medium", children: actionText }),
" ",
defaultPlaceholder
] })
] });
};
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className, children: [
label && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
"label",
{
htmlFor: inputId,
className: "block font-bold text-text-900 mb-2 text-sm",
children: [
label,
" ",
required && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-indicator-error", children: "*" })
]
}
),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
ref: fileInputRef,
id: inputId,
type: "file",
accept,
onChange: handleFileChange,
className: "hidden",
disabled,
"aria-invalid": hasError,
"aria-describedby": getAriaDescribedBy()
}
),
isImagePreview ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"div",
{
className: imageCardClasses,
onDragEnter: handleDragEnter,
onDragLeave: handleDragLeave,
onDragOver: handleDragOver,
onDrop: handleDrop,
children: renderImagePreview()
}
) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"label",
{
htmlFor: inputId,
className: dropzoneClasses,
onDragEnter: handleDragEnter,
onDragLeave: handleDragLeave,
onDragOver: handleDragOver,
onDrop: handleDrop,
children: renderFilePreview()
}
),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "mt-1", children: [
helperText && !errorMessage && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkANT66KVKjs.Text_default, { id: `${inputId}-helper`, size: "xs", className: "text-text-500", children: helperText }),
errorMessage && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkANT66KVKjs.Text_default, { id: `${inputId}-error`, size: "xs", color: "text-indicator-error", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _WarningCircle.WarningCircleIcon, { size: 14 }),
" ",
errorMessage
] })
] })
] });
}
exports.FileDropzone = FileDropzone;
//# sourceMappingURL=chunk-ZAKVMSVA.js.map