UNPKG

@chalkbooks/react-doc-viewer

Version:
516 lines (403 loc) 13.3 kB
[![npm-version](https://img.shields.io/npm/v/@cyntler/react-doc-viewer.svg)](https://www.npmjs.com/package/@cyntler/react-doc-viewer) [![npm-download](https://img.shields.io/npm/dt/@cyntler/react-doc-viewer.svg)](https://www.npmjs.com/package/@cyntler/react-doc-viewer) # @cyntler/react-doc-viewer File viewer for React. > This is a fork of https://github.com/Alcumus/react-doc-viewer (inactivity for a long time) ## Supported file types | Extension | MIME Type | | --------- | ---------------------------------------------------------------------------------- | | bmp | image/bmp | | csv | text/csv | | odt | application/vnd.oasis.opendocument.text | | doc | application/msword | | docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document | | gif | image/gif | | htm | text/htm | | html | text/html | | jpg | image/jpg | | jpeg | image/jpeg | | pdf | application/pdf | | png | image/png | | ppt | application/vnd.ms-powerpoint | | pptx | applicatiapplication/vnd.openxmlformats-officedocument.presentationml.presentation | | tiff | image/tiff | | txt | text/plain | | xls | application/vnd.ms-excel | | xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet | ## Storybook Demo https://cyntler.github.io/react-doc-viewer ## Installation Use one of the package managers for Node.js. ```bash npm i @cyntler/react-doc-viewer # or yarn add @cyntler/react-doc-viewer ``` ## Usage > **Warning:** _By default the component height will expand and contract to the current loaded file. The width will expand to fill the parent._ ### Basic DocViewer requires at least an array of document objects to function. Each document object must have a uri to a file, either a url that returns a file or a local file. ```tsx import DocViewer, { DocViewerRenderers } from "@cyntler/react-doc-viewer"; function App() { const docs = [ { uri: "https://url-to-my-pdf.pdf" }, // Remote file { uri: require("./example-files/pdf.pdf") }, // Local File ]; return <DocViewer documents={docs} pluginRenderers={DocViewerRenderers} />; } ``` ### Initial Active Document By default, the first item in your `documents` array will be displayed after the component is rendered. However, there is a prop `initialActiveDocument` that you can point to the initial document that should be displayed. ```tsx import DocViewer, { DocViewerRenderers } from "@cyntler/react-doc-viewer"; const App = () => { const docs = [ { uri: "https://url-to-my-pdf.pdf" }, // Remote file { uri: require("./example-files/pdf.pdf") }, // Local File ]; return ( <DocViewer documents={docs} initialActiveDocument={docs[1]} pluginRenderers={DocViewerRenderers} /> ); }; ``` ### Control over the displayed document From version **1.11.0** you can control the displayed document through two props: `activeDocument` and `onDocumentChange`. ```jsx const DocViewerControlOverDisplayedDocument = () => { const docs = [ { uri: "https://url-to-my-pdf.pdf" }, // Remote file { uri: require("./example-files/pdf.pdf") }, // Local File ]; const [activeDocument, setActiveDocument] = useState(docs[0]); const handleDocumentChange = (document) => { setActiveDocument(document); }; return ( <> <DocViewer documents={docs} activeDocument={activeDocument} onDocumentChange={handleDocumentChange} /> </> ); }; ``` ### Displaying blob/uploaded documents Since **v1.6.2** you can use documents in the form of blobs, which allows you to e.g. display uploaded files. ```jsx const DocViewerWithInputApp = () => { const [selectedDocs, setSelectedDocs] = useState<File[]>([]); return ( <> <input type="file" accept=".pdf" multiple onChange={(el) => el.target.files?.length && setSelectedDocs(Array.from(el.target.files)) } /> <DocViewer documents={selectedDocs.map((file) => ({ uri: window.URL.createObjectURL(file), fileName: file.name, }))} pluginRenderers={DocViewerRenderers} /> </> ); }; ``` ### Included Renderers To use the included renderers. `DocViewerRenderers` is an Array of all the included renderers. ```tsx import DocViewer, { DocViewerRenderers } from "@cyntler/react-doc-viewer"; <DocViewer pluginRenderers={DocViewerRenderers} {/* ... */} />; ``` Or you can import individual renderers. ```tsx import DocViewer, { PDFRenderer, PNGRenderer } from "@cyntler/react-doc-viewer"; <DocViewer pluginRenderers={[PDFRenderer, PNGRenderer]} {/* ... */} />; ``` ### Custom Renderer To create a custom renderer, that will just exist for your project. ```tsx import React from "react"; import DocViewer from "@cyntler/react-doc-viewer"; const MyCustomPNGRenderer: DocRenderer = ({ mainState: { currentDocument }, }) => { if (!currentDocument) return null; return ( <div id="my-png-renderer"> <img id="png-img" src={currentDocument.fileData as string} /> </div> ); }; MyCustomPNGRenderer.fileTypes = ["png", "image/png"]; MyCustomPNGRenderer.weight = 1; ``` And supply it to `pluginRenderers` inside an `Array`. ```tsx import DocViewer, { DocViewerRenderers } from "@cyntler/react-doc-viewer"; <DocViewer pluginRenderers={[MyCustomPNGRenderer]} documents={ [ // ... ] } />; ``` ### Custom File Loader If you need to prevent the actual loading of the file by `@cyntler/react-doc-viewer`.<br> You can decorate your custom renderer with a callback to do as you wish. e.g. Load the file yourself in an iFrame. ```tsx MyCustomPNGRenderer.fileLoader = ({ documentURI, signal, fileLoaderComplete, }) => { myCustomFileLoaderCode().then(() => { // Whenever you have finished you must call fileLoaderComplete() to remove the loading animation fileLoaderComplete(); }); }; ``` ## Theme You can provide a theme object with one or all of the available properties. ```xml <DocViewer documents={docs} theme={{ primary: "#5296d8", secondary: "#ffffff", tertiary: "#5296d899", textPrimary: "#ffffff", textSecondary: "#5296d8", textTertiary: "#00000099", disableThemeScrollbar: false, }} /> ``` ## Custom pre-fetch HTTP Verb Some services (such as AWS) provide URLs that works only for one pre-configured verb. By default, `@cyntler/react-doc-viewer` fetches document metadata through a `HEAD` request in order to guess its `Content-Type`. If you need to have a specific verb for the pre-fetching, use the `prefetchMethod` option on the DocViewer: ```tsx import DocViewer, { DocViewerRenderers } from "@cyntler/react-doc-viewer"; <DocViewer prefetchMethod="GET" />; ``` ## Custom Request Headers Provide request headers, i.e. for authenticating with an API etc. ```tsx const headers = { "X-Access-Token": "1234567890", "My-Custom-Header": "my-custom-value", }; <DocViewer documents={docs} prefetchMethod="GET" requestHeaders={headers} />; ``` ## Internationalization (i18n) From **v1.6.0** you can pass the `language` prop to the `DocViewer` component to get translated sentences and words that can be displayed by this library. ```xml <DocViewer documents={docs} language="pl" /> ``` The translations are based on the `.json` files that can be found in the `src/locales` directory. ## Styling Any styling applied to the `<DocViewer>` component, is directly applied to the main `div` container. #### CSS Class ```xml <DocViewer documents={docs} className="my-doc-viewer-style" /> ``` #### CSS Class Default Override Each component / div already has a DOM id that can be used to style any part of the document viewer. ```css #react-doc-viewer #header-bar { background-color: #faf; } ``` #### React Inline ```xml <DocViewer documents={docs} style={{ width: 500, height: 500 }} /> ``` #### Styled Components ```tsx import styled from "styled-components"; // ... <MyDocViewer documents={docs} />; // ... const MyDocViewer = styled(DocViewer)` border-radius: 10px; `; ``` ## Using DocViewerRef Since **v1.13.0** you can control the display of the document with `reference`. ```tsx import DocViewer, { DocViewerRef } from "@cyntler/react-doc-viewer"; export const UsingRef = () => { const docViewerRef = useRef<DocViewerRef>(null); return ( <> <div> <button onClick={() => docViewerRef?.current?.prev()}> Prev Document By Ref </button> <button onClick={() => docViewerRef?.current?.next()}> Next Document By Ref </button> </div> <DocViewer ref={docViewerRef} documents={docs} config={{ header: { disableHeader: true } }} /> </> ); }; ``` ## Config You can provide a config object, which configures parts of the component as required. ```tsx <DocViewer documents={docs} config={{ header: { disableHeader: false, disableFileName: false, retainURLParams: false, }, csvDelimiter: ",", // "," as default, pdfZoom: { defaultZoom: 1.1, // 1 as default, zoomJump: 0.2, // 0.1 as default, }, pdfVerticalScrollByDefault: true, // false as default }} /> ``` ### Overriding Header Component You can pass a callback function to `config.header.overrideComponent` that returns a React Element. The function's parameters will be populated and usable, this function will also be re-called whenever the mainState updates. Parameters include the state object from the main component, and document navigation functions for `previousDocument` and `nextDocument`. Example: ```tsx const MyHeader: IHeaderOverride = (state, previousDocument, nextDocument) => { if (!state.currentDocument || state.config?.header?.disableFileName) { return null; } return ( <> <div>{state.currentDocument.uri || ""}</div> <div> <button onClick={previousDocument} disabled={state.currentFileNo === 0}> Previous Document </button> <button onClick={nextDocument} disabled={state.currentFileNo >= state.documents.length - 1} > Next Document </button> </div> </> ); }; <DocViewer pluginRenderers={DocViewerRenderers} documents={ { // ... } } config={{ header: { overrideComponent: MyHeader, }, }} />; ``` ### Overriding Loading Renderer You can pass a callback function to `config.loadingRenderer.overrideComponent` that returns a React Element. Example: ```tsx const MyLoadingRenderer = ({ document, fileName }) => { const fileText = fileName || document?.fileType || ""; if (fileText) { return <div>Loading Renderer ({fileText})...</div>; } return <div>Loading Renderer...</div>; }; <DocViewer pluginRenderers={DocViewerRenderers} documents={ { // ... } } config={{ loadingRenderer: { overrideComponent: MyLoadingRenderer, }, }} />; ``` By default, the loading component is rendered if document loading process takes more than 500 ms. You can change this time value or disable this feature to make the component display immediately: ```tsx const MyLoadingRenderer = ({ document, fileName }) => { ... }; <DocViewer pluginRenderers={DocViewerRenderers} documents={ { // ... } } config={{ loadingRenderer: { overrideComponent: MyLoadingRenderer, showLoadingTimeout: false, // false if you want to disable or number to provide your own value (ms) }, }} />; ``` ### Overriding No Renderer (Error) You can pass a callback function to `config.noRenderer.overrideComponent` that returns a React Element. Example: ```tsx const MyNoRenderer = ({ document, fileName }) => { const fileText = fileName || document?.fileType || ""; if (fileText) { return <div>No Renderer Error! ({fileText})</div>; } return <div>No Renderer Error!</div>; }; <DocViewer pluginRenderers={DocViewerRenderers} documents={ { // ... } } config={{ noRenderer: { overrideComponent: MyNoRenderer, }, }} />; ```