UNPKG

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
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 }))] }))); };