UNPKG

zentrixui

Version:

ZentrixUI - A modern, highly customizable and accessible React file upload component library with multiple variants, JSON-based configuration, and excellent developer experience.

166 lines (165 loc) 6.89 kB
import { jsxs, jsx } from "react/jsx-runtime"; import { cn } from "../../../utils/theme.js"; import { ProgressBar } from "../progress/progress-bar.js"; import { StatusIndicator } from "../progress/status-indicator.js"; import { LoadingSpinner } from "../progress/loading-spinner.js"; import { AccessibilityAnnouncer } from "../progress/accessibility-announcer.js"; import { ErrorFeedback } from "./error-feedback.js"; const UploadFeedback = ({ files = [], isUploading, showIndividualProgress = true, showOverallProgress = true, showStatusIndicators = true, showFileNames = true, enableAccessibilityAnnouncements = true, showErrorFeedback = true, showAccessibilityAnnouncer = true, layout = "default", progressSize = "md", statusSize = "md", maxVisibleFiles = 10, className, onRetry, onRemove }) => { if (!files || files.length === 0) { return null; } const totalProgress = files.reduce((sum, file) => sum + file.progress, 0); const overallProgress = files.length > 0 ? totalProgress / files.length : 0; const completedFiles = files.filter((f) => f.status === "success").length; const failedFiles = files.filter((f) => f.status === "error").length; const uploadingFiles = files.filter((f) => f.status === "uploading").length; const overallStatus = failedFiles > 0 && !isUploading ? "error" : completedFiles === files.length && files.length > 0 ? "success" : "default"; const visibleFiles = files.slice(0, maxVisibleFiles); const hiddenFilesCount = Math.max(0, files.length - maxVisibleFiles); return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [ showErrorFeedback && /* @__PURE__ */ jsx( ErrorFeedback, { compact: layout === "compact", showAccessibilityAnnouncer } ), enableAccessibilityAnnouncements && /* @__PURE__ */ jsx( AccessibilityAnnouncer, { files, isUploading, announceProgress: true, announceStatus: true, announceErrors: true } ), showOverallProgress && files.length > 1 && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ isUploading && /* @__PURE__ */ jsx(LoadingSpinner, { size: "sm" }), /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: isUploading ? "Uploading files..." : "Upload complete" }) ] }), /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-600", children: [ completedFiles, "/", files.length, " files" ] }) ] }), /* @__PURE__ */ jsx( ProgressBar, { progress: overallProgress, variant: overallStatus, showPercentage: true, "aria-label": `Overall upload progress: ${Math.round(overallProgress)}%` } ), /* @__PURE__ */ jsx("div", { className: "flex justify-between text-xs text-gray-600", children: /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [ completedFiles > 0 && /* @__PURE__ */ jsxs("span", { className: "text-green-600", children: [ "✓ ", completedFiles, " completed" ] }), uploadingFiles > 0 && /* @__PURE__ */ jsxs("span", { className: "text-blue-600", children: [ "↑ ", uploadingFiles, " uploading" ] }), failedFiles > 0 && /* @__PURE__ */ jsxs("span", { className: "text-red-600", children: [ "✗ ", failedFiles, " failed" ] }) ] }) }) ] }), showIndividualProgress && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [ visibleFiles.map((file) => /* @__PURE__ */ jsxs( "div", { className: "flex items-center gap-3 p-3 bg-gray-50 rounded-lg", children: [ showStatusIndicators && /* @__PURE__ */ jsx( StatusIndicator, { status: file.status, size: "sm", "aria-label": `${file.name} status: ${file.status}` } ), /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [ showFileNames && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium truncate", children: file.name }), /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500 ml-2", children: [ (file.size / 1024 / 1024).toFixed(1), " MB" ] }) ] }), (file.status === "uploading" || file.progress > 0) && /* @__PURE__ */ jsx( ProgressBar, { progress: file.progress, size: "sm", variant: file.status === "error" ? "error" : file.status === "success" ? "success" : "default", showPercentage: file.status === "uploading", "aria-label": `${file.name} upload progress: ${file.progress}%` } ), file.status === "error" && file.error && /* @__PURE__ */ jsx("div", { className: "mt-1 text-xs text-red-600", children: file.error }) ] }), /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [ file.status === "error" && onRetry && /* @__PURE__ */ jsx( "button", { onClick: () => onRetry(file.id), className: "text-xs text-blue-600 hover:text-blue-800 px-2 py-1 rounded border border-blue-200 hover:border-blue-300", "aria-label": `Retry upload for ${file.name}`, children: "Retry" } ), onRemove && file.status !== "uploading" && /* @__PURE__ */ jsx( "button", { onClick: () => onRemove(file.id), className: "text-xs text-gray-600 hover:text-red-600 px-2 py-1 rounded border border-gray-200 hover:border-red-300", "aria-label": `Remove ${file.name}`, children: "Remove" } ) ] }) ] }, file.id )), hiddenFilesCount > 0 && /* @__PURE__ */ jsxs("div", { className: "text-center text-sm text-gray-600 py-2", children: [ "... and ", hiddenFilesCount, " more file", hiddenFilesCount === 1 ? "" : "s" ] }) ] }) ] }); }; export { UploadFeedback }; //# sourceMappingURL=upload-feedback.js.map