react-ipdf-viewer
Version:
A lightweight, dependency-free media viewer for PDFs and other media types with advanced controls
281 lines (280 loc) • 12.3 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") {
var link = document.createElement("a");
link.href = src;
link.download = fileName || "download";
link.click();
}
else {
var url_1 = mediaUrl ||
createObjectURL(src, mimeType || "application/octet-stream");
var link = document.createElement("a");
link.href = url_1;
link.download = fileName || "download";
if (url_1.startsWith("blob:")) {
// Ensure Blob URL is revoked after download to avoid memory leaks
link.addEventListener("click", function () {
URL.revokeObjectURL(url_1);
});
}
link.click();
}
}, theme,
// () => {
// const fileUrl =
// typeof src === "string"
// ? src
// : URL.createObjectURL(
// src instanceof Blob
// ? src
// : new Blob([src], {
// type: mimeType || "application/octet-stream",
// })
// );
// const 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);
// const isPDF = fileUrl.includes(".pdf") || mimeType === "application/pdf";
// if (isPDF) {
// printFrame.onload = () => {
// try {
// printFrame.contentWindow?.focus();
// printFrame.contentWindow?.print();
// } catch (err) {
// console.error("Print error:", err);
// } finally {
// setTimeout(() => {
// document.body.removeChild(printFrame);
// if (typeof src !== "string") URL.revokeObjectURL(fileUrl);
// }, 1000);
// }
// };
// // ✅ Set src directly for PDFs — no srcdoc
// printFrame.src = `${fileUrl}#toolbar=0&navpanes=0&scrollbar=0`;
// } else {
// // For images and other types
// printFrame.onload = () => {
// setTimeout(() => {
// printFrame.contentWindow?.focus();
// printFrame.contentWindow?.print();
// setTimeout(() => {
// document.body.removeChild(printFrame);
// if (typeof src !== "string") URL.revokeObjectURL(fileUrl);
// }, 1000);
// }, 500);
// };
// // ✅ srcdoc is fine for images
// printFrame.srcdoc = `
// <!DOCTYPE html>
// <html>
// <head><title>Print</title></head>
// <body style="margin: 0;">
// <img src="${fileUrl}" style="max-width: 100%;" onload="window.print()" />
// </body>
// </html>
// `;
// }
// },
// () => {
// const fileUrl =
// typeof src === "string"
// ? src
// : URL.createObjectURL(
// new Blob([src], { type: mimeType || "application/octet-stream" })
// );
// const frame = document.createElement("iframe");
// frame.style.display = "none";
// document.body.appendChild(frame);
// const cleanUp = () => {
// document.body.removeChild(frame);
// if (typeof src !== "string") URL.revokeObjectURL(fileUrl);
// };
// const isPDF = fileUrl.includes(".pdf") || mimeType === "application/pdf";
// frame.onload = () => {
// frame.contentWindow?.focus();
// frame.contentWindow?.print();
// setTimeout(cleanUp, 1000);
// };
// frame.src = isPDF
// ? `${fileUrl}#toolbar=0`
// : `data:text/html,
// <html>
// <body style="margin:0">
// <img src='${fileUrl}' style='width:100%' onload='window.print()' />
// </body>
// </html>`;
// },
function () {
var fileUrl = typeof src === "string"
? src
: URL.createObjectURL(new Blob([src], { type: mimeType || "application/octet-stream" }));
var frame = document.createElement("iframe");
frame.style.display = "none";
document.body.appendChild(frame);
var isPDF = fileUrl.includes(".pdf") || mimeType === "application/pdf";
frame.src = isPDF
? "".concat(fileUrl, "#toolbar=0")
: "data:text/html,\n <html>\n <body style=\"margin:0\">\n <img src='".concat(fileUrl, "' style='width:100%'>\n </body>\n </html>");
// Wait a little bit for iframe to load, then print directly
setTimeout(function () {
var _a, _b;
(_a = frame.contentWindow) === null || _a === void 0 ? void 0 : _a.focus();
(_b = frame.contentWindow) === null || _b === void 0 ? void 0 : _b.print();
setTimeout(function () {
document.body.removeChild(frame);
if (typeof src !== "string")
URL.revokeObjectURL(fileUrl);
}, 1000);
}, 500); // wait 0.5 sec to let iframe load
}, 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: "80%",
// 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 }))] })));
};