Socket
Socket
Sign inDemoInstall

react-file-previewer

Package Overview
Dependencies
341
Maintainers
1
Versions
36
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.2.3 to 0.3.1

src/utils/useViewportSize.js

445

dist/index.cjs.js

@@ -23,2 +23,54 @@ 'use strict';

function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function () {
var self = this,
args = arguments;
return new Promise(function (resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function _slicedToArray(arr, i) {

@@ -75,3 +127,3 @@ return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();

var setZoomIn = R.converge(R.assoc('scale'), [R.compose(R.min(SCALE_ABSOLUTE_MAX), R.add(SCALE_FACTOR), R.propOr(1, 'scale')), R.identity]);
var setZoomIn = R.converge(R.assoc('scale'), [R.compose(R.map(R.min(SCALE_ABSOLUTE_MAX)), R.map(R.add(SCALE_FACTOR)), R.when(R.is(Number), R.of), R.propOr([1], 'scale')), R.identity]);

@@ -87,3 +139,3 @@ var SCALE_FACTOR$1 = 0.25;

var setZoomOut = R.converge(R.assoc('scale'), [R.compose(R.max(SCALE_ABSOLUTE_MIN), R.subtract(R.__, SCALE_FACTOR$1), R.propOr(1, 'scale')), R.identity]);
var setZoomOut = R.converge(R.assoc('scale'), [R.compose(R.map(R.max(SCALE_ABSOLUTE_MIN)), R.map(R.subtract(R.__, SCALE_FACTOR$1)), R.when(R.is(Number), R.of), R.propOr([1], 'scale')), R.identity]);

@@ -99,19 +151,67 @@ /**

var useViewportSize = function useViewportSize(viewportElem) {
var isClient = (typeof window === "undefined" ? "undefined" : _typeof(window)) === 'object';
var _useState = React.useState({
width: R.pathOr(0, ['current', 'clientWidth'], viewportElem),
height: R.pathOr(0, ['current', 'clientHeight'], viewportElem)
}),
_useState2 = _slicedToArray(_useState, 2),
size = _useState2[0],
setSize = _useState2[1];
React.useEffect(function () {
setSize({
width: R.pathOr(0, ['current', 'clientWidth'], viewportElem),
height: R.pathOr(0, ['current', 'clientHeight'], viewportElem)
});
}, viewportElem);
React.useEffect(function () {
if (!isClient) {
return false;
}
function handleResize() {
setSize({
width: R.pathOr(0, ['current', 'clientWidth'], viewportElem),
height: R.pathOr(0, ['current', 'clientHeight'], viewportElem)
});
}
window.addEventListener('resize', handleResize);
return function () {
return window.removeEventListener('resize', handleResize);
};
}, []);
return size;
};
/**
* Get the `scale` attribute for the "fit to screen" option.
*
* @param {Object} viewportElement
* @param {Object} contentElement
* @param {Object} viewportElem
* @param {Object} contentElem
* @return {Number}
*/
var getFitToScreenScale = function getFitToScreenScale(viewportElement, contentElement) {
// Get the viewport ratio.
var viewportWidth = viewportElement.clientWidth;
var viewportHeight = viewportElement.clientHeight;
var getFitToScreenScale = function getFitToScreenScale(viewportElem, contentElem) {
// Get the preview bar height.
var previewBarHeight = 52; // Get the viewport ratio.
var viewportWidth = viewportElem.width || viewportElem.clientWidth;
var viewportHeight = (viewportElem.height || viewportElem.clientHeight) - previewBarHeight - 35;
var viewportRatio = viewportWidth / viewportHeight; // Get the content ratio.
var contentWidth = contentElement.offsetWidth;
var contentHeight = contentElement.offsetHeight;
var contentRatio = contentWidth / contentHeight; // Get the scaling ratio in `0.25` steps.
var contentWidth = contentElem.width || contentElem.offsetWidth;
var contentHeight = contentElem.height || contentElem.offsetHeight;
var contentRatio = contentWidth / contentHeight;
if (contentHeight > viewportHeight) {
return viewportHeight / contentHeight;
}
if (contentWidth > viewportWidth) {
return viewportWidth / contentHeight;
} // Get the scaling ratio in `0.25` steps.
return Math.round((viewportRatio > contentRatio ? viewportHeight / contentHeight : viewportWidth / contentWidth) * 4) / 4;

@@ -266,6 +366,5 @@ };

var handleLoadSucess = React.useCallback(function (_ref3) {
var numPages = _ref3.numPages;
onLoadSuccess(numPages);
setTotalPages(numPages);
var handleLoadSucess = React.useCallback(function (pdf) {
onLoadSuccess(pdf);
setTotalPages(pdf.numPages);
}, [onLoadSuccess]);

@@ -280,4 +379,4 @@ return React__default.createElement(reactPdf.Document, {

index: index,
scale: file.scale,
onPageChange: onPageChange
onPageChange: onPageChange,
scale: Array.isArray(file.scale) ? file.scale[index] : file.scale
});

@@ -298,12 +397,63 @@ }, totalPages));

/**
* Get the scale for this image.
*
* @param {Object} file
* @return {Number}
*/
var getScale = R.pathOr(1, ['scale', 0]);
/**
* Get the rotation for this image.
*
* @param {Object} file
* @return {Number}
*/
var getRotation = R.propOr(0, 'rotate');
var getResponseLikePDF = function getResponseLikePDF(imgRef) {
var _imgRef$current = imgRef.current,
clientWidth = _imgRef$current.clientWidth,
clientHeight = _imgRef$current.clientHeight;
return {
numPages: 1,
getPage: function getPage() {
return {
getViewport: function getViewport() {
return {
width: clientWidth,
height: clientHeight
};
}
};
}
};
};
/**
* `ImageViewer` react component.
*
* @param {Object} params
* @param {Object} params.file
* @param {String} params.file.url
* @param {String} params.file.data
* @param {Array} params.file.scale
* @param {Number} params.file.rotate
* @param {String} params.file.mimeType
* @param {Function} params.onLoadSuccess
*/
var ImageViewer = function ImageViewer(_ref) {
var file = _ref.file,
onLoadSuccess = _ref.onLoadSuccess;
var imgRef = React.useRef(null);
React.useEffect(function () {
onLoadSuccess(1);
onLoadSuccess(getResponseLikePDF(imgRef));
}, []);
return React__default.createElement("img", {
ref: imgRef,
src: file.url || "data:".concat(file.mimeType, ";base64,").concat(file.data),
style: {
transform: "rotate(".concat(file.rotate || 0, "deg) scale(").concat(file.scale || 1, ")")
transform: "rotate(".concat(getRotation(file), "deg) scale(").concat(getScale(file), ")")
}

@@ -316,5 +466,6 @@ });

url: PropTypes.string,
mimeType: PropTypes.string,
data: PropTypes.string,
name: PropTypes.string
scale: PropTypes.array,
rotate: PropTypes.number,
mimeType: PropTypes.string
}),

@@ -336,12 +487,10 @@ onLoadSuccess: PropTypes.func.isRequired

thumbnail = _ref.thumbnail,
contentRef = _ref.contentRef,
viewportRef = _ref.viewportRef,
onLoadSuccess = _ref.onLoadSuccess,
onPageChange = _ref.onPageChange;
containerRef = _ref.containerRef,
onPageChange = _ref.onPageChange,
onLoadSuccess = _ref.onLoadSuccess;
return React__default.createElement("div", {
ref: viewportRef,
ref: containerRef,
className: classnames(thumbnail ? 'media-thumbnail' : 'preview-content')
}, React__default.createElement("div", {
className: "preview-file",
ref: contentRef
className: "preview-file"
}, isPDF(file) ? React__default.createElement(PDFViewer, {

@@ -365,4 +514,2 @@ file: file,

thumbnail: PropTypes.bool,
contentRef: PropTypes.any,
viewportRef: PropTypes.any,
onPageChange: PropTypes.func.isRequired,

@@ -372,6 +519,17 @@ onLoadSuccess: PropTypes.func.isRequired

/**
* `FilePreviewer` react component.
*
* @param {Object} props
* @param {Function} props.onClick
* @param {Boolean} props.thumbnail
* @param {Object} props.file
* @param {String} props.file.url
* @param {String} props.file.data
* @param {String} props.file.name
* @param {String} props.file.mimeType
* @return {Object}
*/
var FilePreviewer = function FilePreviewer(props) {
var contentRef = React.useRef(null);
var viewportRef = React.useRef(null);
var _useState = React.useState(props.file),

@@ -392,15 +550,22 @@ _useState2 = _slicedToArray(_useState, 2),

React.useEffect(function () {
var f = props.file; // if file passed is uploaded file, handle it correctly
var _useState7 = React.useState([]),
_useState8 = _slicedToArray(_useState7, 2),
originalSizes = _useState8[0],
setOriginalSizes = _useState8[1];
if (props.file && props.file instanceof File) {
f.url = URL.createObjectURL(props.file);
f.mimeType = props.file.type;
}
var _useState9 = React.useState(false),
_useState10 = _slicedToArray(_useState9, 2),
usingFitToScreen = _useState10[0],
setUsingFitToScreen = _useState10[1];
setFile(f);
setCurrentPage(0);
}, [props.file]); // Scroll to the pervious page and update the index.
var viewportRef = React.useRef(null);
var containerRef = React.useRef(null);
var viewportSize = useViewportSize(viewportRef);
/**
* Handler. Scroll to the pervious page and update the index.
*
* @return {Void}
*/
var handlePageUp = function handlePageUp() {
var onPageUp = function onPageUp() {
var previousIndex = R.clamp(0, totalPages, currentPage - 1);

@@ -410,7 +575,12 @@ setCurrentPage(previousIndex);

viewportRef.current.scrollTop = previousPage.offsetTop - 10;
}; // Scroll to the next page and update the index.
containerRef.current.scrollTop = previousPage.offsetTop - 10;
};
/**
* Handler. Scroll to the next page and update the index.
*
* @return {Void}
*/
var handlePageDown = function handlePageDown() {
var onPageDown = function onPageDown() {
var nextIndex = R.clamp(0, totalPages, currentPage + 1);

@@ -420,61 +590,166 @@ setCurrentPage(nextIndex);

viewportRef.current.scrollTop = nextPage.offsetTop - 10;
}; // Handlers for rotate and zooming.
containerRef.current.scrollTop = nextPage.offsetTop - 10;
};
/**
* Handler. Scale all the pages or image
* up by a factor of 0.25.
*
* @return {Void}
*/
var handleZoomIn = function handleZoomIn() {
return setFile(setZoomIn(file));
var onZoomIn = function onZoomIn() {
setUsingFitToScreen(false);
setFile(setZoomIn(file));
};
/**
* Handler. Scale all the pages or image
* down by a factor of 0.25.
*
* @return {Void}
*/
var handleZoomOut = function handleZoomOut() {
return setFile(setZoomOut(file));
var onZoomOut = function onZoomOut() {
setUsingFitToScreen(false);
setFile(setZoomOut(file));
};
/**
* Handler. Rotate the PDF (all pages) or Image.
*
* @return {Void}
*/
var handleRotate = function handleRotate() {
return setFile(setNewRotation(file));
var onRotate = function onRotate() {
setFile(setNewRotation(file));
};
/**
* Handler. Fit to screen.
*
* (This activates the "using fit to screen" mode, which
* makes the document or image resize based on the viewport size.)
*
* @return {Void}
*/
var handleDownload = function handleDownload() {
var onFitToScreen = function onFitToScreen() {
setUsingFitToScreen(true); // Get the "fit to screen" scale.
var newScale = originalSizes.map(function (originalSize) {
return getFitToScreenScale(viewportSize, originalSize);
});
setFile(function (prevValue) {
return R.assoc('scale', newScale, prevValue);
});
};
/**
* Handler. Download the current file, it can be a
* PDF or Image, as `url` or `base64` content.
*
* @return {Void}
*/
var onDownload = function onDownload() {
var url = file.url || "data:".concat(file.mimeType, ";base64,").concat(file.data);
return saveFile(url, file.name || 'download.pdf');
};
/**
* Handler. Callback after the PDF gets
* processed successfully by `react-pdf`.
*
* @param {Object} pdf
* @return {Void}
*/
var handleFitToScreen = function handleFitToScreen() {
// Get the "fit to screen" scale.
var newScale = getFitToScreenScale(viewportRef.current, contentRef.current);
setFile(R.assoc('scale', newScale, file));
};
if (!file) {
return null;
}
var onLoadSuccess = function onLoadSuccess(pdf) {
// Wait until the original sizes gets calculated.
var promises = R.times( /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(i) {
var page;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return pdf.getPage(i + 1);
case 2:
page = _context.sent;
return _context.abrupt("return", R.pick(['width', 'height'], page.getViewport({
scale: 1
})));
case 4:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}(), pdf.numPages); // Set the original sizes for this file.
Promise.all(promises).then(setOriginalSizes);
setTotalPages(pdf.numPages);
setUsingFitToScreen(true);
}; // Set an effect to process the file resizing when the
// viewport sizes changes and it's using "fit to screen".
React.useEffect(function () {
// Check if it's using "fit to screen".
if (usingFitToScreen) {
// Get the "fit to screen" scale.
var newScale = originalSizes.map(function (originalSize) {
return getFitToScreenScale(viewportSize, originalSize);
}); // Update the scale.
setFile(function (prevValue) {
return R.assoc('scale', newScale, prevValue);
});
}
}, [usingFitToScreen, originalSizes, viewportSize]); // Reset all the attributes when the file prop changes.
React.useEffect(function () {
var file = props.file; // if file passed is uploaded file, handle it correctly
if (props.file && props.file instanceof File) {
file.url = URL.createObjectURL(props.file);
file.mimeType = props.file.type;
}
setFile(file);
setTotalPages(1);
setCurrentPage(0);
}, [props.file]);
return React__default.createElement("div", {
id: props.id,
ref: viewportRef,
onClick: props.onClick,
className: "preview-wrapper",
style: {
height: props.height,
width: props.width
}
className: "preview-wrapper"
}, React__default.createElement(PreviewBar, {
onPageUp: handlePageUp,
onPageUp: onPageUp,
onRotate: onRotate,
totalPages: totalPages,
onRotate: handleRotate,
onDownload: onDownload,
onPageDown: onPageDown,
hidden: props.thumbnail,
currentPage: currentPage,
onDownload: handleDownload,
onPageDown: handlePageDown
currentPage: currentPage
}), React__default.createElement(ViewportContent, {
file: file,
contentRef: contentRef,
viewportRef: viewportRef,
containerRef: containerRef,
thumbnail: props.thumbnail,
onLoadSuccess: setTotalPages,
onPageChange: setCurrentPage
onPageChange: setCurrentPage,
onLoadSuccess: onLoadSuccess
}), React__default.createElement(ViewportControl, {
onZoomIn: handleZoomIn,
onZoomIn: onZoomIn,
onZoomOut: onZoomOut,
hidden: props.thumbnail,
onZoomOut: handleZoomOut,
onFitToScreen: handleFitToScreen
onFitToScreen: onFitToScreen
}));

@@ -484,3 +759,2 @@ };

FilePreviewer.propTypes = {
id: PropTypes.any,
file: PropTypes.shape({

@@ -493,9 +767,6 @@ url: PropTypes.string,

onClick: PropTypes.func,
thumbnail: PropTypes.bool,
height: PropTypes.string,
width: PropTypes.string
thumbnail: PropTypes.bool
};
FilePreviewer.defaultProps = {
height: '100%',
width: '100%'
onClick: function onClick() {}
};

@@ -502,0 +773,0 @@

@@ -1,5 +0,5 @@

import { converge, assoc, compose, min, add, propOr, identity, max, subtract, __, modulo, times, either, o, endsWith, propEq, clamp } from 'ramda';
import { converge, assoc, compose, map, min, add, when, is, of, propOr, identity, max, subtract, __, modulo, pathOr, times, either, o, endsWith, propEq, clamp, pick } from 'ramda';
import saveFile from 'file-saver';
import PropTypes from 'prop-types';
import React, { useState, useCallback, useEffect, useRef } from 'react';
import React, { useState, useEffect, useCallback, useRef } from 'react';
import ChevronUp from 'mdi-material-ui/ChevronUp';

@@ -16,2 +16,54 @@ import ChevronDown from 'mdi-material-ui/ChevronDown';

function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function () {
var self = this,
args = arguments;
return new Promise(function (resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function _slicedToArray(arr, i) {

@@ -68,3 +120,3 @@ return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();

var setZoomIn = converge(assoc('scale'), [compose(min(SCALE_ABSOLUTE_MAX), add(SCALE_FACTOR), propOr(1, 'scale')), identity]);
var setZoomIn = converge(assoc('scale'), [compose(map(min(SCALE_ABSOLUTE_MAX)), map(add(SCALE_FACTOR)), when(is(Number), of), propOr([1], 'scale')), identity]);

@@ -80,3 +132,3 @@ var SCALE_FACTOR$1 = 0.25;

var setZoomOut = converge(assoc('scale'), [compose(max(SCALE_ABSOLUTE_MIN), subtract(__, SCALE_FACTOR$1), propOr(1, 'scale')), identity]);
var setZoomOut = converge(assoc('scale'), [compose(map(max(SCALE_ABSOLUTE_MIN)), map(subtract(__, SCALE_FACTOR$1)), when(is(Number), of), propOr([1], 'scale')), identity]);

@@ -92,19 +144,67 @@ /**

var useViewportSize = function useViewportSize(viewportElem) {
var isClient = (typeof window === "undefined" ? "undefined" : _typeof(window)) === 'object';
var _useState = useState({
width: pathOr(0, ['current', 'clientWidth'], viewportElem),
height: pathOr(0, ['current', 'clientHeight'], viewportElem)
}),
_useState2 = _slicedToArray(_useState, 2),
size = _useState2[0],
setSize = _useState2[1];
useEffect(function () {
setSize({
width: pathOr(0, ['current', 'clientWidth'], viewportElem),
height: pathOr(0, ['current', 'clientHeight'], viewportElem)
});
}, viewportElem);
useEffect(function () {
if (!isClient) {
return false;
}
function handleResize() {
setSize({
width: pathOr(0, ['current', 'clientWidth'], viewportElem),
height: pathOr(0, ['current', 'clientHeight'], viewportElem)
});
}
window.addEventListener('resize', handleResize);
return function () {
return window.removeEventListener('resize', handleResize);
};
}, []);
return size;
};
/**
* Get the `scale` attribute for the "fit to screen" option.
*
* @param {Object} viewportElement
* @param {Object} contentElement
* @param {Object} viewportElem
* @param {Object} contentElem
* @return {Number}
*/
var getFitToScreenScale = function getFitToScreenScale(viewportElement, contentElement) {
// Get the viewport ratio.
var viewportWidth = viewportElement.clientWidth;
var viewportHeight = viewportElement.clientHeight;
var getFitToScreenScale = function getFitToScreenScale(viewportElem, contentElem) {
// Get the preview bar height.
var previewBarHeight = 52; // Get the viewport ratio.
var viewportWidth = viewportElem.width || viewportElem.clientWidth;
var viewportHeight = (viewportElem.height || viewportElem.clientHeight) - previewBarHeight - 35;
var viewportRatio = viewportWidth / viewportHeight; // Get the content ratio.
var contentWidth = contentElement.offsetWidth;
var contentHeight = contentElement.offsetHeight;
var contentRatio = contentWidth / contentHeight; // Get the scaling ratio in `0.25` steps.
var contentWidth = contentElem.width || contentElem.offsetWidth;
var contentHeight = contentElem.height || contentElem.offsetHeight;
var contentRatio = contentWidth / contentHeight;
if (contentHeight > viewportHeight) {
return viewportHeight / contentHeight;
}
if (contentWidth > viewportWidth) {
return viewportWidth / contentHeight;
} // Get the scaling ratio in `0.25` steps.
return Math.round((viewportRatio > contentRatio ? viewportHeight / contentHeight : viewportWidth / contentWidth) * 4) / 4;

@@ -259,6 +359,5 @@ };

var handleLoadSucess = useCallback(function (_ref3) {
var numPages = _ref3.numPages;
onLoadSuccess(numPages);
setTotalPages(numPages);
var handleLoadSucess = useCallback(function (pdf) {
onLoadSuccess(pdf);
setTotalPages(pdf.numPages);
}, [onLoadSuccess]);

@@ -273,4 +372,4 @@ return React.createElement(Document, {

index: index,
scale: file.scale,
onPageChange: onPageChange
onPageChange: onPageChange,
scale: Array.isArray(file.scale) ? file.scale[index] : file.scale
});

@@ -291,12 +390,63 @@ }, totalPages));

/**
* Get the scale for this image.
*
* @param {Object} file
* @return {Number}
*/
var getScale = pathOr(1, ['scale', 0]);
/**
* Get the rotation for this image.
*
* @param {Object} file
* @return {Number}
*/
var getRotation = propOr(0, 'rotate');
var getResponseLikePDF = function getResponseLikePDF(imgRef) {
var _imgRef$current = imgRef.current,
clientWidth = _imgRef$current.clientWidth,
clientHeight = _imgRef$current.clientHeight;
return {
numPages: 1,
getPage: function getPage() {
return {
getViewport: function getViewport() {
return {
width: clientWidth,
height: clientHeight
};
}
};
}
};
};
/**
* `ImageViewer` react component.
*
* @param {Object} params
* @param {Object} params.file
* @param {String} params.file.url
* @param {String} params.file.data
* @param {Array} params.file.scale
* @param {Number} params.file.rotate
* @param {String} params.file.mimeType
* @param {Function} params.onLoadSuccess
*/
var ImageViewer = function ImageViewer(_ref) {
var file = _ref.file,
onLoadSuccess = _ref.onLoadSuccess;
var imgRef = useRef(null);
useEffect(function () {
onLoadSuccess(1);
onLoadSuccess(getResponseLikePDF(imgRef));
}, []);
return React.createElement("img", {
ref: imgRef,
src: file.url || "data:".concat(file.mimeType, ";base64,").concat(file.data),
style: {
transform: "rotate(".concat(file.rotate || 0, "deg) scale(").concat(file.scale || 1, ")")
transform: "rotate(".concat(getRotation(file), "deg) scale(").concat(getScale(file), ")")
}

@@ -309,5 +459,6 @@ });

url: PropTypes.string,
mimeType: PropTypes.string,
data: PropTypes.string,
name: PropTypes.string
scale: PropTypes.array,
rotate: PropTypes.number,
mimeType: PropTypes.string
}),

@@ -329,12 +480,10 @@ onLoadSuccess: PropTypes.func.isRequired

thumbnail = _ref.thumbnail,
contentRef = _ref.contentRef,
viewportRef = _ref.viewportRef,
onLoadSuccess = _ref.onLoadSuccess,
onPageChange = _ref.onPageChange;
containerRef = _ref.containerRef,
onPageChange = _ref.onPageChange,
onLoadSuccess = _ref.onLoadSuccess;
return React.createElement("div", {
ref: viewportRef,
ref: containerRef,
className: classnames(thumbnail ? 'media-thumbnail' : 'preview-content')
}, React.createElement("div", {
className: "preview-file",
ref: contentRef
className: "preview-file"
}, isPDF(file) ? React.createElement(PDFViewer, {

@@ -358,4 +507,2 @@ file: file,

thumbnail: PropTypes.bool,
contentRef: PropTypes.any,
viewportRef: PropTypes.any,
onPageChange: PropTypes.func.isRequired,

@@ -365,6 +512,17 @@ onLoadSuccess: PropTypes.func.isRequired

/**
* `FilePreviewer` react component.
*
* @param {Object} props
* @param {Function} props.onClick
* @param {Boolean} props.thumbnail
* @param {Object} props.file
* @param {String} props.file.url
* @param {String} props.file.data
* @param {String} props.file.name
* @param {String} props.file.mimeType
* @return {Object}
*/
var FilePreviewer = function FilePreviewer(props) {
var contentRef = useRef(null);
var viewportRef = useRef(null);
var _useState = useState(props.file),

@@ -385,15 +543,22 @@ _useState2 = _slicedToArray(_useState, 2),

useEffect(function () {
var f = props.file; // if file passed is uploaded file, handle it correctly
var _useState7 = useState([]),
_useState8 = _slicedToArray(_useState7, 2),
originalSizes = _useState8[0],
setOriginalSizes = _useState8[1];
if (props.file && props.file instanceof File) {
f.url = URL.createObjectURL(props.file);
f.mimeType = props.file.type;
}
var _useState9 = useState(false),
_useState10 = _slicedToArray(_useState9, 2),
usingFitToScreen = _useState10[0],
setUsingFitToScreen = _useState10[1];
setFile(f);
setCurrentPage(0);
}, [props.file]); // Scroll to the pervious page and update the index.
var viewportRef = useRef(null);
var containerRef = useRef(null);
var viewportSize = useViewportSize(viewportRef);
/**
* Handler. Scroll to the pervious page and update the index.
*
* @return {Void}
*/
var handlePageUp = function handlePageUp() {
var onPageUp = function onPageUp() {
var previousIndex = clamp(0, totalPages, currentPage - 1);

@@ -403,7 +568,12 @@ setCurrentPage(previousIndex);

viewportRef.current.scrollTop = previousPage.offsetTop - 10;
}; // Scroll to the next page and update the index.
containerRef.current.scrollTop = previousPage.offsetTop - 10;
};
/**
* Handler. Scroll to the next page and update the index.
*
* @return {Void}
*/
var handlePageDown = function handlePageDown() {
var onPageDown = function onPageDown() {
var nextIndex = clamp(0, totalPages, currentPage + 1);

@@ -413,61 +583,166 @@ setCurrentPage(nextIndex);

viewportRef.current.scrollTop = nextPage.offsetTop - 10;
}; // Handlers for rotate and zooming.
containerRef.current.scrollTop = nextPage.offsetTop - 10;
};
/**
* Handler. Scale all the pages or image
* up by a factor of 0.25.
*
* @return {Void}
*/
var handleZoomIn = function handleZoomIn() {
return setFile(setZoomIn(file));
var onZoomIn = function onZoomIn() {
setUsingFitToScreen(false);
setFile(setZoomIn(file));
};
/**
* Handler. Scale all the pages or image
* down by a factor of 0.25.
*
* @return {Void}
*/
var handleZoomOut = function handleZoomOut() {
return setFile(setZoomOut(file));
var onZoomOut = function onZoomOut() {
setUsingFitToScreen(false);
setFile(setZoomOut(file));
};
/**
* Handler. Rotate the PDF (all pages) or Image.
*
* @return {Void}
*/
var handleRotate = function handleRotate() {
return setFile(setNewRotation(file));
var onRotate = function onRotate() {
setFile(setNewRotation(file));
};
/**
* Handler. Fit to screen.
*
* (This activates the "using fit to screen" mode, which
* makes the document or image resize based on the viewport size.)
*
* @return {Void}
*/
var handleDownload = function handleDownload() {
var onFitToScreen = function onFitToScreen() {
setUsingFitToScreen(true); // Get the "fit to screen" scale.
var newScale = originalSizes.map(function (originalSize) {
return getFitToScreenScale(viewportSize, originalSize);
});
setFile(function (prevValue) {
return assoc('scale', newScale, prevValue);
});
};
/**
* Handler. Download the current file, it can be a
* PDF or Image, as `url` or `base64` content.
*
* @return {Void}
*/
var onDownload = function onDownload() {
var url = file.url || "data:".concat(file.mimeType, ";base64,").concat(file.data);
return saveFile(url, file.name || 'download.pdf');
};
/**
* Handler. Callback after the PDF gets
* processed successfully by `react-pdf`.
*
* @param {Object} pdf
* @return {Void}
*/
var handleFitToScreen = function handleFitToScreen() {
// Get the "fit to screen" scale.
var newScale = getFitToScreenScale(viewportRef.current, contentRef.current);
setFile(assoc('scale', newScale, file));
};
if (!file) {
return null;
}
var onLoadSuccess = function onLoadSuccess(pdf) {
// Wait until the original sizes gets calculated.
var promises = times( /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(i) {
var page;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return pdf.getPage(i + 1);
case 2:
page = _context.sent;
return _context.abrupt("return", pick(['width', 'height'], page.getViewport({
scale: 1
})));
case 4:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}(), pdf.numPages); // Set the original sizes for this file.
Promise.all(promises).then(setOriginalSizes);
setTotalPages(pdf.numPages);
setUsingFitToScreen(true);
}; // Set an effect to process the file resizing when the
// viewport sizes changes and it's using "fit to screen".
useEffect(function () {
// Check if it's using "fit to screen".
if (usingFitToScreen) {
// Get the "fit to screen" scale.
var newScale = originalSizes.map(function (originalSize) {
return getFitToScreenScale(viewportSize, originalSize);
}); // Update the scale.
setFile(function (prevValue) {
return assoc('scale', newScale, prevValue);
});
}
}, [usingFitToScreen, originalSizes, viewportSize]); // Reset all the attributes when the file prop changes.
useEffect(function () {
var file = props.file; // if file passed is uploaded file, handle it correctly
if (props.file && props.file instanceof File) {
file.url = URL.createObjectURL(props.file);
file.mimeType = props.file.type;
}
setFile(file);
setTotalPages(1);
setCurrentPage(0);
}, [props.file]);
return React.createElement("div", {
id: props.id,
ref: viewportRef,
onClick: props.onClick,
className: "preview-wrapper",
style: {
height: props.height,
width: props.width
}
className: "preview-wrapper"
}, React.createElement(PreviewBar, {
onPageUp: handlePageUp,
onPageUp: onPageUp,
onRotate: onRotate,
totalPages: totalPages,
onRotate: handleRotate,
onDownload: onDownload,
onPageDown: onPageDown,
hidden: props.thumbnail,
currentPage: currentPage,
onDownload: handleDownload,
onPageDown: handlePageDown
currentPage: currentPage
}), React.createElement(ViewportContent, {
file: file,
contentRef: contentRef,
viewportRef: viewportRef,
containerRef: containerRef,
thumbnail: props.thumbnail,
onLoadSuccess: setTotalPages,
onPageChange: setCurrentPage
onPageChange: setCurrentPage,
onLoadSuccess: onLoadSuccess
}), React.createElement(ViewportControl, {
onZoomIn: handleZoomIn,
onZoomIn: onZoomIn,
onZoomOut: onZoomOut,
hidden: props.thumbnail,
onZoomOut: handleZoomOut,
onFitToScreen: handleFitToScreen
onFitToScreen: onFitToScreen
}));

@@ -477,3 +752,2 @@ };

FilePreviewer.propTypes = {
id: PropTypes.any,
file: PropTypes.shape({

@@ -486,9 +760,6 @@ url: PropTypes.string,

onClick: PropTypes.func,
thumbnail: PropTypes.bool,
height: PropTypes.string,
width: PropTypes.string
thumbnail: PropTypes.bool
};
FilePreviewer.defaultProps = {
height: '100%',
width: '100%'
onClick: function onClick() {}
};

@@ -495,0 +766,0 @@

2

package.json
{
"name": "react-file-previewer",
"version": "0.2.3",
"version": "0.3.1",
"main": "dist/index.cjs.js",

@@ -5,0 +5,0 @@ "module": "dist/index.es.js",

import * as R from 'ramda';
import saveFile from 'file-saver';
import PropTypes from 'prop-types';
import React, { useEffect, useRef, useState } from 'react';
import React, { useState, useEffect, useRef } from 'react';

@@ -9,2 +9,3 @@ import setZoomIn from './utils/setZoomIn';

import setNewRotation from './utils/setNewRotation';
import useViewportSize from './utils/useViewportSize';
import getFitToScreenScale from './utils/getFitToScreenScale';

@@ -16,28 +17,32 @@

/**
* `FilePreviewer` react component.
*
* @param {Object} props
* @param {Function} props.onClick
* @param {Boolean} props.thumbnail
* @param {Object} props.file
* @param {String} props.file.url
* @param {String} props.file.data
* @param {String} props.file.name
* @param {String} props.file.mimeType
* @return {Object}
*/
const FilePreviewer = props => {
const contentRef = useRef(null);
const viewportRef = useRef(null);
const [file, setFile] = useState(props.file);
const [totalPages, setTotalPages] = useState(1);
const [currentPage, setCurrentPage] = useState(0);
const [originalSizes, setOriginalSizes] = useState([]);
const [usingFitToScreen, setUsingFitToScreen] = useState(false);
useEffect(() => {
let f = props.file;
const viewportRef = useRef(null);
const containerRef = useRef(null);
const viewportSize = useViewportSize(viewportRef);
// if file passed is uploaded file, handle it correctly
if (props.file && props.file instanceof File) {
f.url = URL.createObjectURL(props.file);
f.mimeType = props.file.type;
}
setFile(f);
setCurrentPage(0);
}, [props.file]);
// Scroll to the pervious page and update the index.
const handlePageUp = () => {
/**
* Handler. Scroll to the pervious page and update the index.
*
* @return {Void}
*/
const onPageUp = () => {
const previousIndex = R.clamp(0, totalPages, currentPage - 1);

@@ -52,7 +57,11 @@

// Scroll the viewport to the page position.
viewportRef.current.scrollTop = previousPage.offsetTop - 10;
containerRef.current.scrollTop = previousPage.offsetTop - 10;
};
// Scroll to the next page and update the index.
const handlePageDown = () => {
/**
* Handler. Scroll to the next page and update the index.
*
* @return {Void}
*/
const onPageDown = () => {
const nextIndex = R.clamp(0, totalPages, currentPage + 1);

@@ -65,47 +74,128 @@

// Scroll the viewport to the page position.
viewportRef.current.scrollTop = nextPage.offsetTop - 10;
containerRef.current.scrollTop = nextPage.offsetTop - 10;
};
// Handlers for rotate and zooming.
const handleZoomIn = () => setFile(setZoomIn(file));
const handleZoomOut = () => setFile(setZoomOut(file));
const handleRotate = () => setFile(setNewRotation(file));
/**
* Handler. Scale all the pages or image
* up by a factor of 0.25.
*
* @return {Void}
*/
const onZoomIn = () => {
setUsingFitToScreen(false);
setFile(setZoomIn(file));
};
const handleDownload = () => {
const url = file.url || `data:${file.mimeType};base64,${file.data}`;
return saveFile(url, file.name || 'download.pdf');
/**
* Handler. Scale all the pages or image
* down by a factor of 0.25.
*
* @return {Void}
*/
const onZoomOut = () => {
setUsingFitToScreen(false);
setFile(setZoomOut(file));
};
const handleFitToScreen = () => {
/**
* Handler. Rotate the PDF (all pages) or Image.
*
* @return {Void}
*/
const onRotate = () => {
setFile(setNewRotation(file));
};
/**
* Handler. Fit to screen.
*
* (This activates the "using fit to screen" mode, which
* makes the document or image resize based on the viewport size.)
*
* @return {Void}
*/
const onFitToScreen = () => {
setUsingFitToScreen(true);
// Get the "fit to screen" scale.
const newScale = getFitToScreenScale(
viewportRef.current,
contentRef.current,
const newScale = originalSizes.map(originalSize =>
getFitToScreenScale(viewportSize, originalSize),
);
setFile(R.assoc('scale', newScale, file));
setFile(prevValue => R.assoc('scale', newScale, prevValue));
};
if (!file) {
return null;
}
/**
* Handler. Download the current file, it can be a
* PDF or Image, as `url` or `base64` content.
*
* @return {Void}
*/
const onDownload = () => {
const url = file.url || `data:${file.mimeType};base64,${file.data}`;
return saveFile(url, file.name || 'download.pdf');
};
/**
* Handler. Callback after the PDF gets
* processed successfully by `react-pdf`.
*
* @param {Object} pdf
* @return {Void}
*/
const onLoadSuccess = pdf => {
// Wait until the original sizes gets calculated.
const promises = R.times(async i => {
// Wait until the original sizes gets calculated.
const page = await pdf.getPage(i + 1);
return R.pick(['width', 'height'], page.getViewport({ scale: 1 }));
}, pdf.numPages);
// Set the original sizes for this file.
Promise.all(promises).then(setOriginalSizes);
setTotalPages(pdf.numPages);
setUsingFitToScreen(true);
};
// Set an effect to process the file resizing when the
// viewport sizes changes and it's using "fit to screen".
useEffect(() => {
// Check if it's using "fit to screen".
if (usingFitToScreen) {
// Get the "fit to screen" scale.
const newScale = originalSizes.map(originalSize =>
getFitToScreenScale(viewportSize, originalSize),
);
// Update the scale.
setFile(prevValue => R.assoc('scale', newScale, prevValue));
}
}, [usingFitToScreen, originalSizes, viewportSize]);
// Reset all the attributes when the file prop changes.
useEffect(() => {
const file = props.file;
// if file passed is uploaded file, handle it correctly
if (props.file && props.file instanceof File) {
file.url = URL.createObjectURL(props.file);
file.mimeType = props.file.type;
}
setFile(file);
setTotalPages(1);
setCurrentPage(0);
}, [props.file]);
return (
<div
id={props.id}
onClick={props.onClick}
className="preview-wrapper"
style={{
height: props.height,
width: props.width,
}}
>
<div ref={viewportRef} onClick={props.onClick} className="preview-wrapper">
<PreviewBar
onPageUp={handlePageUp}
onPageUp={onPageUp}
onRotate={onRotate}
totalPages={totalPages}
onRotate={handleRotate}
onDownload={onDownload}
onPageDown={onPageDown}
hidden={props.thumbnail}
currentPage={currentPage}
onDownload={handleDownload}
onPageDown={handlePageDown}
/>

@@ -115,14 +205,13 @@

file={file}
contentRef={contentRef}
viewportRef={viewportRef}
containerRef={containerRef}
thumbnail={props.thumbnail}
onLoadSuccess={setTotalPages}
onPageChange={setCurrentPage}
onLoadSuccess={onLoadSuccess}
/>
<ViewportControl
onZoomIn={handleZoomIn}
onZoomIn={onZoomIn}
onZoomOut={onZoomOut}
hidden={props.thumbnail}
onZoomOut={handleZoomOut}
onFitToScreen={handleFitToScreen}
onFitToScreen={onFitToScreen}
/>

@@ -134,3 +223,2 @@ </div>

FilePreviewer.propTypes = {
id: PropTypes.any,
file: PropTypes.shape({

@@ -144,11 +232,8 @@ url: PropTypes.string,

thumbnail: PropTypes.bool,
height: PropTypes.string,
width: PropTypes.string,
};
FilePreviewer.defaultProps = {
height: '100%',
width: '100%',
onClick: () => {},
};
export default FilePreviewer;

@@ -1,7 +0,52 @@

import React, { useEffect } from 'react';
import { propOr, pathOr } from 'ramda';
import PropTypes from 'prop-types';
import React, { useRef, useEffect } from 'react';
/**
* Get the scale for this image.
*
* @param {Object} file
* @return {Number}
*/
const getScale = pathOr(1, ['scale', 0]);
/**
* Get the rotation for this image.
*
* @param {Object} file
* @return {Number}
*/
const getRotation = propOr(0, 'rotate');
const getResponseLikePDF = imgRef => {
const { clientWidth, clientHeight } = imgRef.current;
return {
numPages: 1,
getPage: () => ({
getViewport: () => ({
width: clientWidth,
height: clientHeight,
}),
}),
};
};
/**
* `ImageViewer` react component.
*
* @param {Object} params
* @param {Object} params.file
* @param {String} params.file.url
* @param {String} params.file.data
* @param {Array} params.file.scale
* @param {Number} params.file.rotate
* @param {String} params.file.mimeType
* @param {Function} params.onLoadSuccess
*/
const ImageViewer = ({ file, onLoadSuccess }) => {
const imgRef = useRef(null);
useEffect(() => {
onLoadSuccess(1);
onLoadSuccess(getResponseLikePDF(imgRef));
}, []);

@@ -11,5 +56,6 @@

<img
ref={imgRef}
src={file.url || `data:${file.mimeType};base64,${file.data}`}
style={{
transform: `rotate(${file.rotate || 0}deg) scale(${file.scale || 1})`,
transform: `rotate(${getRotation(file)}deg) scale(${getScale(file)})`,
}}

@@ -23,5 +69,6 @@ />

url: PropTypes.string,
data: PropTypes.string,
scale: PropTypes.array,
rotate: PropTypes.number,
mimeType: PropTypes.string,
data: PropTypes.string,
name: PropTypes.string,
}),

@@ -28,0 +75,0 @@ onLoadSuccess: PropTypes.func.isRequired,

@@ -1,2 +0,1 @@

import React, { useEffect, useCallback, useState } from 'react';
import * as R from 'ramda';

@@ -6,2 +5,3 @@ import PropTypes from 'prop-types';

import { useInView } from 'react-intersection-observer';
import React, { useEffect, useCallback, useState } from 'react';

@@ -26,5 +26,5 @@ const PDFPage = ({ index, onPageChange, scale = 1 }) => {

const handleLoadSucess = useCallback(
({ numPages }) => {
onLoadSuccess(numPages);
setTotalPages(numPages);
pdf => {
onLoadSuccess(pdf);
setTotalPages(pdf.numPages);
},

@@ -45,4 +45,4 @@ [onLoadSuccess],

index={index}
scale={file.scale}
onPageChange={onPageChange}
scale={Array.isArray(file.scale) ? file.scale[index] : file.scale}
/>

@@ -49,0 +49,0 @@ ),

/**
* Get the `scale` attribute for the "fit to screen" option.
*
* @param {Object} viewportElement
* @param {Object} contentElement
* @param {Object} viewportElem
* @param {Object} contentElem
* @return {Number}
*/
const getFitToScreenScale = (viewportElement, contentElement) => {
const getFitToScreenScale = (viewportElem, contentElem) => {
// Get the preview bar height.
const previewBarHeight = 52;
// Get the viewport ratio.
const viewportWidth = viewportElement.clientWidth;
const viewportHeight = viewportElement.clientHeight;
const viewportWidth = viewportElem.width || viewportElem.clientWidth;
const viewportHeight =
(viewportElem.height || viewportElem.clientHeight) - previewBarHeight - 35;
const viewportRatio = viewportWidth / viewportHeight;
// Get the content ratio.
const contentWidth = contentElement.offsetWidth;
const contentHeight = contentElement.offsetHeight;
const contentWidth = contentElem.width || contentElem.offsetWidth;
const contentHeight = contentElem.height || contentElem.offsetHeight;
const contentRatio = contentWidth / contentHeight;
if (contentHeight > viewportHeight) {
return viewportHeight / contentHeight;
}
if (contentWidth > viewportWidth) {
return viewportWidth / contentHeight;
}
// Get the scaling ratio in `0.25` steps.

@@ -20,0 +34,0 @@ return (

@@ -14,5 +14,6 @@ import * as R from 'ramda';

R.compose(
R.min(SCALE_ABSOLUTE_MAX),
R.add(SCALE_FACTOR),
R.propOr(1, 'scale'),
R.map(R.min(SCALE_ABSOLUTE_MAX)),
R.map(R.add(SCALE_FACTOR)),
R.when(R.is(Number), R.of),
R.propOr([1], 'scale'),
),

@@ -19,0 +20,0 @@ R.identity,

@@ -14,5 +14,6 @@ import * as R from 'ramda';

R.compose(
R.max(SCALE_ABSOLUTE_MIN),
R.subtract(R.__, SCALE_FACTOR),
R.propOr(1, 'scale'),
R.map(R.max(SCALE_ABSOLUTE_MIN)),
R.map(R.subtract(R.__, SCALE_FACTOR)),
R.when(R.is(Number), R.of),
R.propOr([1], 'scale'),
),

@@ -19,0 +20,0 @@ R.identity,

@@ -23,12 +23,11 @@ import React from 'react';

thumbnail,
contentRef,
viewportRef,
containerRef,
onPageChange,
onLoadSuccess,
onPageChange,
}) => (
<div
ref={viewportRef}
ref={containerRef}
className={classnames(thumbnail ? 'media-thumbnail' : 'preview-content')}
>
<div className="preview-file" ref={contentRef}>
<div className="preview-file">
{isPDF(file) ? (

@@ -55,4 +54,2 @@ <PDFViewer

thumbnail: PropTypes.bool,
contentRef: PropTypes.any,
viewportRef: PropTypes.any,
onPageChange: PropTypes.func.isRequired,

@@ -59,0 +56,0 @@ onLoadSuccess: PropTypes.func.isRequired,

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc