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
JavaScript
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