“This is the fifth day of my participation in the First Challenge 2022. For details: First Challenge 2022”

preface

The most powerful rich text editor in the last post? TinyMCE series of articles [2] article introduced how to encapsulate TinyMCE rich text editor components, this time to talk about how to use the rich text component after editing text information articles and add file links to achieve preview function requirements, which has little to do with TinyMCE itself, but because of its use caused disputes. It’s one thing after another. As soon as the need is fulfilled, other needs that extend from this need come along, giving you no time to breathe. As long as you rest, you will affect the boss to buy a luxury house to raise the luxury car time, this sin can be big 😛, life is so difficult.

Demand background

I’ll talk about the knowledge base project I mentioned earlier, because the TinyMCE rich text editor component is used more extensively here. In the future, we will start with the figure above and describe what the requirements are from the figure, which is easy for the readers to understand. In this way, I will not be confused about what to do and what problems to solve after sharing a lot of information. OK, see the picture below:

This is the graphic information we use TinyMCE rich text editor to edit. Now we need to do:

1) Click the picture to preview. 2) Copy the attached link into the editor. Click to judge the file type and preview directly or view it after downloadingCopy the code

Thinking about? I believe that some friends have encountered rich text content need to click on the picture to preview, but do not know how you do it? And then the file links in the rich text editor can still open? Let alone preview it; In fact, this is our daily life. Once we find that we haven’t done it before or can’t find relevant examples, we will give up. What I’m trying to say is that it’s not necessary. When you’re curious enough, you’ll find a way to solve a problem. Here’s what we did:

The development of

Development we are divided into preview component development and attachment component development, to provide code and ideas can be directly for small partners to use or borrow.

Image Preview component

For the image preview component, we choose The React-Viewer plug-in, which is worth recommending. All requirements for image preview in our project are based on this plug-in, which provides powerful and flexible functions and can be configured according to your project requirements. Learn how to use the React-Viewer plugin on your own. If you don’t know, I can write an article on how to use the React-Viewer plugin.

Install the react-Viewer plugin:

yarn add react-viewer
Copy the code

Consider this: we can’t add click events when editing content generated by rich text editors, so how can clicking on an image trigger the preview component?

In fact, this question was first addressed in my previous post in 2022: We can use the special Children property provided by React to wrap the rich text editor’s content in the Children image preview component. Then you can add events to the rich text content, and you can click on the image.

Preview the component code in the image. Create imageViewer.tsx file:

import React, {ReactElement, useState} from "react"; import Viewer from "react-viewer"; import {filePreview} from ".. /attachment/Attachment"; import './style.less'; interface ImageViewerProps { className? : string; / / the name of the class the children? : ReactElement; disabled? : boolean; // Disable preview attachmentDisabled? : boolean; } /** * @props * @constructor */ const ImageViewer = (props: ImageViewerProps) => { const { className, children, disabled = false, attachmentDisabled = false } = props; const [imgList, setImgList] = useState<any[]>([]); const [viewerVisible, setViewerVisible] = useState<boolean>(false); @param e */ const imgPreview = (e: any) => {if (disabled) {return; } e.stopPropagation(); const { target } = e; // Customize attachment hyperlink if (! attachmentDisabled && target.getAttribute('data-editor-link') === 'true') { e.preventDefault(); filePreview(target.getAttribute('data-editor-link-text'), target.getAttribute('href')); return; } if (target && target.tagName.toLowerCase() == 'img') { setImgList([{src: target.src}]); setViewerVisible(true); } } return ( <> <div className={className} onClick={imgPreview}> {children} </div> <Viewer className="img-viewer-component" visible={viewerVisible} onClose={() => setViewerVisible(false) } images={imgList} zIndex={2000} /> </> ) } export default ImageViewer;Copy the code

Create style.less style file:

.img-viewer-component { transition-duration: .01s ! important; }Copy the code

In fact, the style file is optional, our project needs to set the transition effect, so we need to create a style file to set.

The above code is all the code of the image preview component. If you are careful, you can see that this component also relies on the filePreview method of the filePreview in an Attachment component. As mentioned in the requirements above, users can paste the link of the uploaded file into the rich text editor. That rich text content will be generated file links, and the preview component of our packaging is rich text editor content, links all files also has the click event, so we will according to the file link url file type, if accord with our allowed file types can preview attachments for subsequent operations.

if (! attachmentDisabled && target.getAttribute('data-editor-link') === 'true') { e.preventDefault(); filePreview(target.getAttribute('data-editor-link-text'), target.getAttribute('href')); return; }Copy the code

The tag we click is a unique tag set in our attachment, that is, whether the tag has data-editor-link attribute. If so, please directly open a browser for preview, otherwise it will prompt that preview cannot be done. This part of the logic and code can be seen in the attachment preview component wrapper below. The two components need to be used together, but if they do not want to be used together, you can see from the above code that we have also done this by adding a attachmentDisabled property, If you don’t need attachment preview, just pass true and you won’t follow the attachment logic.

I think the image preview component should be clear, now that it is wrapped, let’s see how to use it:

Import ImageViewer from ".. /.. /component/imageViewer/ImageViewer"; <ImageViewer className="bit-html-content"> <div dangerouslySetInnerHTML={{__html: logContent}}></div> </ImageViewer>Copy the code

Yes, it is so simple, the introduction of use is done, is so strong 👍👏

Attachment Preview component

Continue to strike while the iron is hot, or go far 👻. Directly on the code:

Create attachment. TSX file:

import React, {useState} from "react"; import {ObjTest} from ".. /.. /util/common"; Import {Button, message, Modal, Tag} from "antd"; import { PaperClipOutlined, VerticalAlignBottomOutlined, } from '@ant-design/icons'; import {fileDownload} from ".. /.. /util/request"; Import {globalUploadAPI} from ".. /.. /config/api"; Import './style.less'; Const previewFileTypes = [' TXT ', 'PDF ',' JPG ', 'JPEG ',' PNG ', 'GIF ']; // previewFileTypes = [' TXT ',' PDF ', 'JPG ',' JPEG ', 'PNG ',' GIF ']; interface DataSource { name: string; // Attachment name (with file suffix) URL: string; } interface AttachmentProps {className? : string; contentClassName? : string; dataSource: DataSource[]; } /** * download popup component * @param name * @param url * @constructor */ const DownLoadModal = ({name, url, modal}) => { const [loading, setLoading] = useState<boolean>(false); /** * download file * @param name * @param url */ const download = (name: string, url: string) => {setLoading(true); fileDownload('/download', {fileName: name, fileUrlPath: url}, {baseURL: globalUploadAPI}).then((res: any) => { modal.destroy(); modal = null; Message.success (' download successful '); }).finally(() => { setLoading(false); }) } return ( <div className="flexic flexc mt30"> <Button className="button-red" size="small" style={{width: 100}} icon={<VerticalAlignBottomOutlined />} loading={loading} onClick={() => download(name, @param url */ export const filePreview = (name: string, url: string) => {if (! Url) {message.error(' invalid download link '); return; } / / retrieves the file type let fileType = url. The split ('. '). Slice (1) [0]; / / TXT, PDF files can be opened page preview the if (previewFileTypes. Includes fileType ()) {window. The open return (url, "_blank"); } let modal = Modal.confirm({}); Modal. update({className: 'attachment-download-modal', title: 'This file cannot be previewed, please download and view ', Closable: true, centered: true, icon: null, content: <DownLoadModal name={name} url={url} modal={modal} /> }) } export default Attachment;Copy the code

Create style.less style file:

.custom-attachment-uploader { .ant-upload-list-text-container { transition: none; } .link-btn { font-size: 14px; color: #0A89FF; } .close-btn { color: #ff4d4f; } } .attachment-download-modal { .ant-modal-confirm-btns { display: none; } } .attachement-group-list { .attachment-list-item { font-size: 14px; font-weight: 400; color: #545FD6; line-height: 30px; letter-spacing: 1px; span:hover { cursor: pointer; text-decoration: underline; }}}Copy the code

The attachment package contains: DownLoadModal download popover component and a filePreview filePreview method. The main idea is to use the DownLoadModal component to prompt the user to download and view the file when the file link is not an allowed preview file type.

So far the main code that extends the need for preview images and file links using TinyMCE rich text editor is shown above, and can be used directly by those who need it. As for some configuration if you are in the code to read the article with your heart, it must be accordingly configuration can achieve the same effect as with me, I was out of try to make friends to directly copy the past will be able to use his mind to write these articles, but if you want to completely master it carefully read articles and code, so there must be a harvest.

Basic method tools

We have encapsulated some methods in the above article, so what our friends see is directly quoted. In order to avoid friends directly copying the past and not being able to use it, I decided to post the corresponding code, so that my friends would not scold me for not being able to use it once and for all.

As mentioned above, setting a unique tag with the data-editor-link attribute is the following code. In fact, we use the copy and paste CopyToClipboard component to set the file link to a unique tag so that we can distinguish between a file link and a hyperlink added by the rich text plug-in.

<CopyToClipboard text={`<a data-editor-link="true" data-editor-link-text="${v.name}" href="${v.url}" Target = "_blank" > ${v.n ame} < / a > `} onCopy = {() = > message. Success ({content: 'copy success, duration: <span className="link-btn plr15 HCP "> </span> </CopyToClipboard>Copy the code

Other tools code:

Returns {Boolean} */ export const ObjTest = new class {private str: string = ''; private getTargetStr(target) { this.str = Object.prototype.toString.call(target).toLowerCase(); } // object isObj(target: any) {this.getTargetStr(target); return this.str === '[object object]'; } // function isFunc(target: any) {this.getTargetStr(target); return this.str === '[object function]'; } // array isArray(target: any) {this.getTargetStr(target); return this.str === '[object array]'; } isNotEmptyArray(target: any) { this.getTargetStr(target); if (this.isArray(target) && target.length > 0) { return true; } return false; } // string isStr(target: any) {this.getTargetStr(target); return this.str === '[object string]'; } // isNumber(target: any) {this.getTargetStr(target); return this.str === '[object number]'; } // undefined isUndefined(target: any) {this.getTargetStr(target); return this.str === '[object undefined]'; } // Null pointer isNull(target: any) {this.getTargetStr(target); return this.str === '[object null]'; } // Invalid number isNan(target: any) {this.getTargetStr(target); return this.str === '[object number]' && isNaN(target); } // Valid json string isJSONStr(target: any) {if (this.isstr (target)) {try {let res = json.parse (target); if (this.isObj(res) || this.isArray(res)) { return true; } return false; }catch (e) { return false; } } return false; } // Return true isEmptyObject(target: any) { if (this.isObj(target) && Object.keys(target).length > 0) { return false; } return true; }}Copy the code
/** * export function fileDownload(url: string, body: any, options: IRequestOptions = null) : Promise<any> { const { method = 'post', withToken = true } = options; return instance({ url, data: (method.toLowerCase() == 'post' ? body : {}), params: (method.toLowerCase() == 'get' ? body : {}), method: 'post', baseURL: globalAPI, timeout: 200000, headers: withToken ? GetHeaders () : ", responseType: 'blob', // paramsSerializer: (params: any) => { return encodeGetParams(params); },... Options,}). Then (res => {// get filename const {filename} = res.headers; / / compatible with IE and Edge if (" ActiveXObject in window "| |. The navigator userAgent. IndexOf (" Edge") > 1) { window.navigator.msSaveBlob(res.data, decodeURIComponent(filename)); return res; } let reader = new FileReader() reader.readAsDataURL(res.data) reader.onload = (e: any) => { let a = document.createElement('a'); a.download = decodeURIComponent(filename); a.href = e.target.result document.body.appendChild(a); a.click(); document.body.removeChild(a); } return res; }); }Copy the code

So far, the introduction of TinyMCE editor, use, packaging and extended preview requirements are all talked about, I believe that if you read this series of articles will have a certain gain, then I write this series of articles also achieved. This series of articles are the knowledge accumulated after I finish the online work of the real needs, for those who have not used TinyMCE rich text editing, there will be a new choice in the future work. In fact, there is an extended thought here, but we are not required to do this, but I think there is an extended meaning, that is, how to click on an image, pick up all the images and put them into the React-Viewer plug-in to switch left and right to view?

If you are careful, you can see that after clicking on the image, we only set up one image for the React-Viewer plugin, so we can write a method here to get all the image addresses of the img tag in the content. Set images to be a collection of images to get to the point where you can switch between one image and preview all the images. The methods are not provided here, only the ideas.

Finally, thanks for reading the whole series, 3Q! Other component packaging methods in the work will be introduced later, so stay tuned.

Past wonderful articles

  • The most powerful rich text editor? TinyMCE Series [2]
  • The most powerful rich text editor? TinyMCE Series of articles [1]
  • React project to implement a Checkbox style effect component
  • 2022 First update: 3 Progress bar effects in front end projects
  • Front-end Tech Trends 2022: You don’t come in and see how to earn it a small target?
  • If ancient have programmers writing summaries, probably is such | 2021 year-end summary
  • Front-end development specifications in front end team practice
  • Javascript high-level constructors
  • Javascript high-level inheritance
  • Recursion and closure

After the language

Guys, if you find this article helpful, click 👍 or ➕. In addition, if there are questions or do not understand the part of this article, you are welcome to comment on the comment section, we discuss together.