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.

104 lines (103 loc) 3.85 kB
import { jsx } from "react/jsx-runtime"; import { useRef, useEffect } from "react"; const AccessibilityAnnouncer = ({ files, isUploading, announceProgress = true, announceStatus = true, announceErrors = true, className }) => { const liveRegionRef = useRef(null); const previousFilesRef = useRef([]); const announcementTimeoutRef = useRef(); const announce = (message, priority = "polite") => { if (liveRegionRef.current) { if (announcementTimeoutRef.current) { clearTimeout(announcementTimeoutRef.current); } liveRegionRef.current.setAttribute("aria-live", priority); liveRegionRef.current.textContent = ""; announcementTimeoutRef.current = setTimeout(() => { if (liveRegionRef.current) { liveRegionRef.current.textContent = message; } }, 100); } }; useEffect(() => { const previousFiles = previousFilesRef.current; const currentFiles = files; if (currentFiles.length > previousFiles.length && announceStatus) { const newFilesCount = currentFiles.length - previousFiles.length; announce( `${newFilesCount} file${newFilesCount === 1 ? "" : "s"} selected for upload`, "polite" ); } if (!previousFiles.some((f) => f.status === "uploading") && currentFiles.some((f) => f.status === "uploading") && announceStatus) { announce("Upload started", "polite"); } currentFiles.forEach((currentFile) => { const previousFile = previousFiles.find((f) => f.id === currentFile.id); if (previousFile && previousFile.status !== currentFile.status) { if (currentFile.status === "success" && announceStatus) { announce(`${currentFile.name} uploaded successfully`, "polite"); } else if (currentFile.status === "error" && announceErrors) { const errorMessage = currentFile.error || "Upload failed"; announce(`${currentFile.name} upload failed: ${errorMessage}`, "assertive"); } } if (previousFile && announceProgress && currentFile.status === "uploading" && currentFile.progress > 0) { const currentMilestone = Math.floor(currentFile.progress / 25) * 25; const previousMilestone = Math.floor(previousFile.progress / 25) * 25; if (currentMilestone > previousMilestone && currentMilestone > 0) { announce(`${currentFile.name} ${currentMilestone}% uploaded`, "polite"); } } }); const allCompleted = currentFiles.length > 0 && currentFiles.every((f) => f.status === "success" || f.status === "error"); const wasUploading = previousFiles.some((f) => f.status === "uploading"); if (allCompleted && wasUploading && announceStatus) { const successCount = currentFiles.filter((f) => f.status === "success").length; const errorCount = currentFiles.filter((f) => f.status === "error").length; if (errorCount === 0) { announce(`All ${successCount} files uploaded successfully`, "polite"); } else { announce( `Upload complete: ${successCount} successful, ${errorCount} failed`, "assertive" ); } } previousFilesRef.current = [...currentFiles]; }, [files, announceProgress, announceStatus, announceErrors]); useEffect(() => { return () => { if (announcementTimeoutRef.current) { clearTimeout(announcementTimeoutRef.current); } }; }, []); return /* @__PURE__ */ jsx( "div", { ref: liveRegionRef, className, role: "status", "aria-live": "polite", "aria-atomic": "true", style: { position: "absolute", left: "-10000px", width: "1px", height: "1px", overflow: "hidden" } } ); }; export { AccessibilityAnnouncer }; //# sourceMappingURL=accessibility-announcer.js.map