⚠️ Supplement: My requirement is that I need to upload the file to the backend first, so I get the url address to display, for markdown and txt files need to use fetch to get them first, and for other displays just use the url link directly.


Different documents are implemented in different ways, the following classification is explained, in total, is divided into the following categories:

  1.  Own label file: png、jpg、jpeg、audio、video
  2.  Text-only documents: markdown & txt

  3. office type of file: doc、xlsx、ppt

  4. embed Introduce the document: pdf
  5.   iframe : Introduction of an external full website

 Own label file: png、jpg、jpeg、audio、video


For image, audio and video previews, you can directly use the corresponding tags as follows:

 Picture: png、jpg、jpeg

 Sample code:

 <img src={url} key={docId} alt={name} width="100%" />;

 The preview results are as follows:

 Audio: audio

 Sample code:

<audio ref={audioRef} controls controlsList="nodownload" style={{ width: '100%' }}>
  <track kind="captions" />
  <source src={url} type="audio/mpeg" />
</audio>

 The preview results are as follows:

 Video: video

 Sample code:

<video ref={videoRef} controls muted controlsList="nodownload" style={{ width: '100%' }}>
  <track kind="captions" />
  <source src={url} type="video/mp4" />
</video>

 The preview results are as follows:

 Complete code on the positioning of audio and video:

import React, { useRef, useEffect } from 'react';

interface IProps {
  type: 'audio' | 'video';
  url: string;
  timeInSeconds: number;
}

function AudioAndVideo(props: IProps) {
  const { type, url, timeInSeconds } = props;
  const videoRef = useRef<HTMLVideoElement>(null);
  const audioRef = useRef<HTMLAudioElement>(null);

  useEffect(() => {
     const secondsTime = timeInSeconds / 1000;
    if (type === 'audio' && audioRef.current) {
      audioRef.current.currentTime = secondsTime;
    }
    if (type === 'video' && videoRef.current) {
      videoRef.current.currentTime = secondsTime;
    }
  }, [type, timeInSeconds]);

  return (
    <div>
      {type === 'audio' ? (
        <audio ref={audioRef} controls controlsList="nodownload" style={{ width: '100%' }}>
          <track kind="captions" />
          <source src={url} type="audio/mpeg" />
        </audio>
      ) : (
        <video ref={videoRef} controls muted controlsList="nodownload" style={{ width: '100%' }}>
          <track kind="captions" />
          <source src={url} type="video/mp4" />
        </video>
      )}
    </div>
  );
}

export default AudioAndVideo;

 Text-only documents: markdown & txt


For a file of type markdown、txt , if you get a file of type url , you can’t display it directly, you need to request the content and then display it.

  markdown file


When displaying the markdown file, you need to fulfill the requirements of font highlighting, code highlighting, if there is font highlighting, you need to scroll to the location of the font, and if there is an external link, you need to open a new tab page and then open it again.

 Two libraries need to be introduced:


marked : It serves to convert (parse) markdown text to HTML .


highlight : It allows developers to highlight code on web pages.

 Code implementation of font highlighting:

 The style of highlighting can be defined in the interline style

  const highlightAndMarkFirst = (text: string, highlightText: string) => {
    let firstMatchDone = false;
    const regex = new RegExp(`(${highlightText})`, 'gi');
    return text.replace(regex, (match) => {
      if (!firstMatchDone) {
        firstMatchDone = true;
        return `<span id='first-match' style="color: red;">${match}</span>`;
      }
      return `<span style="color: red;">${match}</span>`;
    });
  };

 Code implementation of code highlighting:

 You need to use the library hljs to do the conversion

marked.use({
    renderer: {
      code(code, infostring) {
        const validLang = !!(infostring && hljs.getLanguage(infostring));
        const highlighted = validLang
          ? hljs.highlight(code, { language: infostring, ignoreIllegals: true }).value
          : code;
        return `<pre><code class="hljs ${infostring}">${highlighted}</code></pre>`;
      } 
    },
  });

 Code implementation of the link to jump to the new tab page.

marked.use({
   renderer: {
       link(href, title, text) {
        const isExternal = !href.startsWith('/') && !href.startsWith('#');
        if (isExternal) {
          return `<a href="${href}" title="${title}" target="_blank" rel="noopener noreferrer">${text}</a>`;
        }
        return `<a href="${href}" title="${title}">${text}</a>`;
      },
    },
});

 Code implementation of scrolling to the highlighted position:

 It needs to be coupled with the code highlighting method above

const firstMatchElement = document.getElementById('first-match');
if (firstMatchElement) {
    firstMatchElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
}

 The complete code is below:


The entry docUrl is the on-line ur l address of the markdown file, and searchText is what needs to be highlighted.

import React, { useEffect, useState, useRef } from 'react';
import { marked } from 'marked';
import hljs from 'highlight.js';

const preStyle = {
  width: '100%',
  maxHeight: '64vh',
  minHeight: '64vh',
  overflow: 'auto',
};

 function MarkdownViewer({ docUrl, searchText }: { docUrl: string; searchText: string }) {
  const [markdown, setMarkdown] = useState('');
  const markdownRef = useRef<HTMLDivElement | null>(null);

  const highlightAndMarkFirst = (text: string, highlightText: string) => {
    let firstMatchDone = false;
    const regex = new RegExp(`(${highlightText})`, 'gi');
    return text.replace(regex, (match) => {
      if (!firstMatchDone) {
        firstMatchDone = true;
        return `<span id='first-match' style="color: red;">${match}</span>`;
      }
      return `<span style="color: red;">${match}</span>`;
    });
  };

  useEffect(() => {
     fetch(docUrl)
      .then((response) => response.text())
      .then((text) => {
        const highlightedText = searchText ? highlightAndMarkFirst(text, searchText) : text;
        setMarkdown(highlightedText);
      })
      .catch((error) => console.error('2:', error));
  }, [searchText, docUrl]);

  useEffect(() => {
    if (markdownRef.current) {
       marked.use({
        renderer: {
          code(code, infostring) {
            const validLang = !!(infostring && hljs.getLanguage(infostring));
            const highlighted = validLang
              ? hljs.highlight(code, { language: infostring, ignoreIllegals: true }).value
              : code;
            return `<pre><code class="hljs ${infostring}">${highlighted}</code></pre>`;
          },
           link(href, title, text) {
            const isExternal = !href.startsWith('/') && !href.startsWith('#');
            if (isExternal) {
              return `<a href="${href}" title="${title}" target="_blank" rel="noopener noreferrer">${text}</a>`;
            }
            return `<a href="${href}" title="${title}">${text}</a>`;
          },
        },
      });
      const htmlContent = marked.parse(markdown);
      markdownRef.current!.innerHTML = htmlContent as string;
       const firstMatchElement = document.getElementById('first-match');
      if (firstMatchElement) {
        firstMatchElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
      }
    }
  }, [markdown]);

  return (
    <div style={preStyle}>
      <div ref={markdownRef} />
    </div>
  );
}

export default MarkdownViewer;

 The preview results are as follows:

  txt File preview display

 Supports highlighting and scrolling to a specific position

 Support for highlighted code:

  function highlightText(text: string) {
    if (!searchText.trim()) return text;
    const regex = new RegExp(`(${searchText})`, 'gi');
    return text.replace(regex, `<span style="color: red">$1</span>`);
  }

 Full Code:

import React, { useEffect, useState, useRef } from 'react';
import { preStyle } from './config';

function TextFileViewer({ docurl, searchText }: { docurl: string; searchText: string }) {
  const [paragraphs, setParagraphs] = useState<string[]>([]);
  const targetRef = useRef<HTMLDivElement | null>(null);

  function highlightText(text: string) {
    if (!searchText.trim()) return text;
    const regex = new RegExp(`(${searchText})`, 'gi');
    return text.replace(regex, `<span style="color: red">$1</span>`);
  }

  useEffect(() => {
    fetch(docurl)
      .then((response) => response.text())
      .then((text) => {
        const highlightedText = highlightText(text);
        const paras = highlightedText
          .split('\n')
          .map((para) => para.trim())
          .filter((para) => para);
        setParagraphs(paras);
      })
      .catch((error) => {
        console.error('1:', error);
      });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [docurl, searchText]);

  useEffect(() => {
     const timer = setTimeout(() => {
      if (targetRef.current) {
        targetRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' });
      }
    }, 100);

    return () => clearTimeout(timer);
  }, [paragraphs]);

  return (
    <div style={preStyle}>
      {paragraphs.map((para: string, index: number) => {
        const paraKey = para + index;

         const isTarget = para.includes(`>${searchText}<`);
        return (
          <p key={paraKey} ref={isTarget && !targetRef.current ? targetRef : null}>
            <div dangerouslySetInnerHTML={{ __html: para }} />
          </p>
        );
      })}
    </div>
  );
}

export default TextFileViewer;

 The preview results are as follows:



office type of file: doc、xlsx、ppt


doc、xlsx、ppt For the preview of the file, it is enough to use the online preview link at office + the online url of our file.


About positioning: with this method I temporarily try is not able to locate the page number, so the positioning function I take is the back-end will be office file into pdf , and then locate, if just pure display, ignore this problem can be.

 Sample code:

<iframe
    src={`https://view.officeapps.live.com/op/view.aspx?src=${url}`}
    width="100%"
    height="500px"
    frameBorder="0"
></iframe>

 The preview results are as follows:



embed Introduce the document: pdf


In the pdf document preview, you can use embed , this httpsUrl is the link address of your pdf document

 Sample code:

 <embed src={`${httpsUrl}`} style={preStyle} key={`${httpsUrl}`} />;


Regarding the positioning, it is actually the page number spliced on the address sourcePage which is as follows:

 const httpsUrl = sourcePage
        ? `${doc.url}#page=${sourcePage}`
        : doc.url;
        
<embed src={`${httpsUrl}`} style={preStyle} key={`${httpsUrl}`} />;    
        

 The preview results are as follows:


  iframe : Introduction of an external full website


In addition to the various files above, we also need to preview some external URLs, then we need to use the iframe method

 Sample code:

 <iframe
    title="网址"
    width="100%"
    height="100%"
    src={doc.url}      
    allow="microphone;camera;midi;encrypted-media;"/>

 

By lzz

Leave a Reply

Your email address will not be published. Required fields are marked *