react-ipdf-viewer
Version:
A lightweight, dependency-free media viewer for PDFs and other media types with advanced controls
233 lines (232 loc) • 10.9 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect, useRef, useMemo } from "react";
import { useViewerControls } from "../hooks/useViewerControls";
import { Toolbar } from "./Toolbar";
// import { PDFRenderer } from './PDFRenderer';
import PDFRenderer from "./PDFRenderer";
import { ImageRenderer, VideoRenderer, AudioRenderer, UnsupportedRenderer, } from "./MediaRenderer";
import { detectMediaType } from "../utils/mediaTypeDetector";
import { createObjectURL } from "../utils/blobHelpers";
export var ReactIPdfViewer = function (_a) {
var src = _a.src, mimeType = _a.mimeType, fileName = _a.fileName, onError = _a.onError, _b = _a.showControls, showControls = _b === void 0 ? true : _b, _c = _a.defaultZoom, defaultZoom = _c === void 0 ? 1 : _c, _d = _a.allowDownload, allowDownload = _d === void 0 ? true : _d, _e = _a.allowPrint, allowPrint = _e === void 0 ? true : _e, _f = _a.allowRotate, allowRotate = _f === void 0 ? true : _f, _g = _a.allowFullScreen, allowFullScreen = _g === void 0 ? true : _g, _h = _a.theme, theme = _h === void 0 ? "light" : _h, _j = _a.rotateValue, rotateValue = _j === void 0 ? 0 : _j, _k = _a.autoHeight, autoHeight = _k === void 0 ? true : _k, className = _a.className, style = _a.style, renderToolbar = _a.renderToolbar;
var _l = useState(null), mediaUrl = _l[0], setMediaUrl = _l[1];
var _m = useState("unsupported"), mediaType = _m[0], setMediaType = _m[1];
var _o = useState("100%"), containerHeight = _o[0], setContainerHeight = _o[1];
var containerRef = useRef(null);
var controls = useViewerControls(defaultZoom, function () {
if (typeof src === "string") {
// Ensure the src is a valid URL and that it's handled properly
var link = document.createElement("a");
link.href = src;
link.download = fileName || "download";
// Trigger the click event in a user-interaction context (e.g., click handler)
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
else {
// If `src` is a Blob or Media source
var url_1 = mediaUrl ||
createObjectURL(src, mimeType || "application/octet-stream");
var link = document.createElement("a");
link.href = url_1;
link.download = fileName || "download";
// For Blobs, ensure the URL is created properly and that the MIME type is appropriate
if (url_1.startsWith("blob:")) {
// Ensure Blob URL is revoked after download to avoid memory leaks
link.addEventListener("click", function () {
URL.revokeObjectURL(url_1);
});
}
// Trigger the click event for download (in a user interaction context)
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}, theme, function () {
var fileUrl = typeof src === "string"
? src
: URL.createObjectURL(src instanceof Blob
? src
: new Blob([src], {
type: mimeType || "application/octet-stream",
}));
var printFrame = document.createElement("iframe");
printFrame.style.position = "fixed";
printFrame.style.right = "0";
printFrame.style.bottom = "0";
printFrame.style.width = "0";
printFrame.style.height = "0";
printFrame.style.border = "0";
printFrame.style.visibility = "hidden";
document.body.appendChild(printFrame);
var isPDF = fileUrl.includes(".pdf") || mimeType === "application/pdf";
if (isPDF) {
printFrame.onload = function () {
var _a, _b;
try {
(_a = printFrame.contentWindow) === null || _a === void 0 ? void 0 : _a.focus();
(_b = printFrame.contentWindow) === null || _b === void 0 ? void 0 : _b.print();
}
catch (err) {
console.error("Print error:", err);
}
finally {
setTimeout(function () {
document.body.removeChild(printFrame);
if (typeof src !== "string")
URL.revokeObjectURL(fileUrl);
}, 1000);
}
};
// ✅ Set src directly for PDFs — no srcdoc
printFrame.src = "".concat(fileUrl, "#toolbar=0&navpanes=0&scrollbar=0");
}
else {
// For images and other types
printFrame.onload = function () {
setTimeout(function () {
var _a, _b;
(_a = printFrame.contentWindow) === null || _a === void 0 ? void 0 : _a.focus();
(_b = printFrame.contentWindow) === null || _b === void 0 ? void 0 : _b.print();
setTimeout(function () {
document.body.removeChild(printFrame);
if (typeof src !== "string")
URL.revokeObjectURL(fileUrl);
}, 1000);
}, 500);
};
// ✅ srcdoc is fine for images
printFrame.srcdoc = "\n <!DOCTYPE html>\n <html>\n <head><title>Print</title></head>\n <body style=\"margin: 0;\">\n <img src=\"".concat(fileUrl, "\" style=\"max-width: 100%;\" onload=\"window.print()\" />\n </body>\n </html>\n ");
}
}, rotateValue);
// Calculate height based on window size
useEffect(function () {
if (!autoHeight) {
setContainerHeight("100%");
return;
}
var updateHeight = function () {
if (containerRef.current) {
var windowHeight = window.innerHeight;
var rect = containerRef.current.getBoundingClientRect();
var height = windowHeight - rect.top - 20; // 20px padding
setContainerHeight("".concat(Math.max(height, 300), "px")); // Minimum 300px
}
};
updateHeight();
window.addEventListener("resize", updateHeight);
return function () { return window.removeEventListener("resize", updateHeight); };
}, [autoHeight]);
// Handle media source changes
useEffect(function () {
var url = null;
var type = detectMediaType(src, mimeType);
setMediaType(type);
if (typeof src !== "string") {
var effectiveMimeType = mimeType;
if (!effectiveMimeType) {
switch (type) {
case "pdf":
effectiveMimeType = "application/pdf";
break;
case "image":
effectiveMimeType = "image/*";
break;
case "video":
effectiveMimeType = "video/*";
break;
case "audio":
effectiveMimeType = "audio/*";
break;
default:
effectiveMimeType = "application/octet-stream";
}
}
url = createObjectURL(src, effectiveMimeType);
setMediaUrl(url);
}
return function () {
if (url) {
URL.revokeObjectURL(url);
}
};
}, [src, mimeType]);
var themeStyles = {
light: {
backgroundColor: "#ffffff",
color: "#000000",
},
dark: {
backgroundColor: "#1e1e1e",
color: "#ffffff",
},
};
var renderMedia = useMemo(function () {
var effectiveUrl = typeof src === "string" ? src : mediaUrl;
if (!effectiveUrl)
return null;
var mediaProps = {
fileUrl: effectiveUrl,
src: effectiveUrl,
mimeType: mimeType || "",
zoom: controls.zoom,
rotation: controls.rotation,
theme: controls.currentTheme,
onError: onError,
};
switch (mediaType) {
case "pdf":
return _jsx(PDFRenderer, __assign({}, mediaProps));
case "image":
return _jsx(ImageRenderer, __assign({}, mediaProps));
case "video":
return _jsx(VideoRenderer, __assign({}, mediaProps));
case "audio":
return _jsx(AudioRenderer, __assign({}, mediaProps));
default:
return _jsx(UnsupportedRenderer, { mimeType: mimeType });
}
}, [
src,
mediaUrl,
mediaType,
mimeType,
controls.zoom,
controls.rotation,
controls.currentTheme,
onError,
]);
return (_jsxs("div", __assign({ ref: containerRef, className: "nexus-viewer ".concat(className || ""), style: __assign(__assign(__assign({ display: "flex", flexDirection: "column", width: "100%",
// width: "60%",
height: containerHeight }, themeStyles[controls.currentTheme]), style), (controls.isFullscreen
? {
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 9999,
}
: {})) }, { children: [showControls &&
(renderToolbar ? (renderToolbar(controls)) : (_jsx(Toolbar, { controls: controls, allowDownload: allowDownload, allowPrint: allowPrint, allowRotate: allowRotate, allowFullScreen: allowFullScreen }))), _jsx("div", __assign({ style: {
flex: 1,
display: "flex",
justifyContent: "center",
alignItems: "center",
overflow: "auto",
// padding: "20px",
backgroundColor: controls.currentTheme === "dark" ? "#2d2d2d" : "#f5f5f5",
} }, { children: renderMedia }))] })));
};