import ImageResize from "@ckeditor/ckeditor5-image/src/imageresize"; import { MutableRefObject, useEffect, useRef, useState } from "react"; import { CgSpinner } from "react-icons/cg"; import { GetAuthToken } from "../../../../lib/graphql/auth.link"; export interface EditorProps extends FormControlProps { minHeight?: string; maxHeight?: string; maxWidth?: string; noBorder?: boolean; } class MyUploadAdapter { loader: any; xhr: XMLHttpRequest; constructor(loader) { this.loader = loader; } upload() { return this.loader.file .then(file => new Promise((resolve, reject) => { this._initRequest(); this._initListeners(resolve, reject, file); this._sendRequest(file); })); } _sendRequest(file) { // Prepare the form data. const data = new FormData(); data.append("upload", file); // Important note: This is the right place to implement security mechanisms // like authentication and CSRF protection. For instance, you can use // XMLHttpRequest.setRequestHeader() to set the request headers containing // the CSRF token generated earlier by your application. // Send the request. this.xhr.send(data); } _initListeners(resolve, reject, file) { const xhr = this.xhr; const loader = this.loader; const genericErrorText = `Couldn't upload file: ${file.name}.`; xhr.addEventListener("error", () => reject(genericErrorText)); xhr.addEventListener("abort", () => reject()); xhr.addEventListener("load", () => { const response = xhr.response; // This example assumes the XHR server's "response" object will come with // an "error" which has its own "message" that can be passed to reject() // in the upload promise. // // Your integration may handle upload errors in a different way so make sure // it is done properly. The reject() function must be called when the upload fails. if (!response || response.error) { return reject(response && response.error ? response.error.message : genericErrorText); } // If the upload is successful, resolve the upload promise with an object containing // at least the "default" URL, pointing to the image on the server. // This URL will be used to display the image in the content. Learn more in the // UploadAdapter#upload documentation. console.log(response); resolve({ default: response.link }); }); // Upload progress when it is supported. The file loader has the #uploadTotal and #uploaded // properties which are used e.g. to display the upload progress bar in the editor // user interface. if (xhr.upload) { xhr.upload.addEventListener("progress", evt => { if (evt.lengthComputable) { loader.uploadTotal = evt.total; loader.uploaded = evt.loaded; } }); } } _initRequest() { const xhr = this.xhr = new XMLHttpRequest(); const token = GetAuthToken(); // Note that your request may look different. It is up to you and your editor // integration to choose the right communication channel. This example uses // a POST request with JSON as a data structure but your configuration // could be different. xhr.open("POST", "/api/file/upload-no-save", true); xhr.setRequestHeader("x-token", token); xhr.responseType = "json"; } } function MyCustomUploadAdapterPlugin(editor) { editor.plugins.get("FileRepository").createUploadAdapter = (loader) => { // Configure the URL to the upload script in your back-end here! return new MyUploadAdapter(loader); }; } export function Editor({ controlClassName = "form-control", className = "flex justify-center bg-gray-100 px-0", maxWidth = "960px", minHeight = "128px", maxHeight = "none", defaultValue = getDefaultValue({}), style = {}, ...props }: EditorProps) { const [value, setValue] = useState(); const [editor, setEditor] = useState(); useEffect(() => { if (props.value !== undefined) { setValue(props.value || defaultValue); } else { setValue(defaultValue); } }, [props.value]); const editorRef: MutableRefObject = useRef(); const [editorLoaded, setEditorLoaded] = useState(false); const { CKEditor, InlineEditor } = editorRef.current || {}; useEffect(() => { editorRef.current = { CKEditor: require("@ckeditor/ckeditor5-react").CKEditor, InlineEditor: require("@lynxerious/ckeditor5-build-mcom"), }; setEditorLoaded(true); }, []); const onChange = (data) => { setValue(data); if (props.onChange) props.onChange(data); }; useEffect(() => { if (editor) { editor.editing.view.change((writer) => { writer.setStyle("min-height", minHeight, editor.editing.view.document.getRoot()); writer.setStyle("max-height", maxHeight, editor.editing.view.document.getRoot()); writer.setStyle("max-width", maxWidth, editor.editing.view.document.getRoot()); writer.setStyle("width", "100%", editor.editing.view.document.getRoot()); writer.setStyle("border-radius", "inherit", editor.editing.view.document.getRoot()); writer.setStyle( "background-color", props.readonly ? "transparent" : "white", editor.editing.view.document.getRoot() ); if (props.noBorder) { writer.setStyle("border", "0 !important", editor.editing.view.document.getRoot()); writer.setStyle("box-shadow", "none !important", editor.editing.view.document.getRoot()); } else { writer.setStyle( "box-shadow", "0 0 4px 1px rgba(0, 0, 0, 0.08)", editor.editing.view.document.getRoot() ); } }); editor.isReadOnly = props.readonly; } }, [props.readonly, minHeight, maxHeight, editor]); return ( <> {editorLoaded ? (
{ onChange(editor.getData()); }} onReady={(editor) => { setEditor(editor); }} />
) : (
Đang tải
)} ); } const getDefaultValue = (props: EditorProps) => { return ""; }; Editor.getDefaultValue = getDefaultValue;