ckeditor/ckeditor5-react

how to install autosave in React, TypeScript project

surzioarmani opened this issue · 1 comments

import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
import { CKEditor } from '@ckeditor/ckeditor5-react';

import { apiPostUploadFile } from '@/api/common/apiComfunc';
import { Alignment } from '@ckeditor/ckeditor5-alignment';
import { Autosave } from '@ckeditor/ckeditor5-autosave';
import { Bold, Code, Italic, Strikethrough, Subscript, Superscript, Underline } from '@ckeditor/ckeditor5-basic-styles';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import viewToPlainText from '@ckeditor/ckeditor5-clipboard/src/utils/viewtoplaintext';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { FindAndReplace } from '@ckeditor/ckeditor5-find-and-replace';
import { Font, FontFamily } from '@ckeditor/ckeditor5-font';
import Heading from '@ckeditor/ckeditor5-heading/src/heading';
import { HorizontalLine } from '@ckeditor/ckeditor5-horizontal-line';
import {
	Image,
	ImageInsert,
	ImageInsertViaUrl,
	ImageResizeEditing,
	ImageResizeHandles,
	ImageUpload,
} from '@ckeditor/ckeditor5-image';
import { Indent } from '@ckeditor/ckeditor5-indent';
import { AutoLink, Link } from '@ckeditor/ckeditor5-link';
import { ListProperties } from '@ckeditor/ckeditor5-list';
import { MediaEmbed } from '@ckeditor/ckeditor5-media-embed';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
import { RemoveFormat } from '@ckeditor/ckeditor5-remove-format';
import { SourceEditing } from '@ckeditor/ckeditor5-source-editing';
import { SpecialCharacters, SpecialCharactersEssentials } from '@ckeditor/ckeditor5-special-characters';
import { Table, TableCaption, TableCellProperties, TableProperties, TableToolbar } from '@ckeditor/ckeditor5-table';
// import { Base64UploadAdapter } from '@ckeditor/ckeditor5-upload';
import { Button, Row } from 'antd';
import DOMPurify from 'dompurify';
interface PropsType {
	data?: any;
	offImage?: any;
	setData?: React.Dispatch<React.SetStateAction<any>>;
	setPlainText?: React.Dispatch<React.SetStateAction<any>>;
}

const Ckeditor = (props: PropsType) => {
	const { setData, offImage, setPlainText, data } = props; // html whole text
	// const [editor, setEditor] = useState<ClassicEditor | null>(null);
	const [isPreview, setIsPreview] = useState(false);
	const [num, setNum] = useState(0); //only text
	const [fileList, setFileList] = useState([]);
	// const autosavePlugin: PluginConstructor<Editor> = Autosave;
	// 다국어
	const { t } = useTranslation();

	const customColorPalette = [
		{
			color: 'hsl(4, 90%, 58%)',
			label: 'Red',
		},
		{
			color: 'hsl(340, 82%, 52%)',
			label: 'Pink',
		},
		{
			color: 'hsl(291, 64%, 42%)',
			label: 'Purple',
		},
		{
			color: 'hsl(262, 52%, 47%)',
			label: 'Deep Purple',
		},
		{
			color: 'hsl(231, 48%, 48%)',
			label: 'Indigo',
		},
		{
			color: 'hsl(207, 90%, 54%)',
			label: 'Blue',
		},
	];

	/**
	 *
	 * @param {any} editor editor
	 * @param {any} editor.plugins editor.plugins
	 * @param {any} editor.plugins.get editor.plugins.get
	 */
	function uploadPlugin(editor: {
		plugins: {
			get: (arg0: string) => {
				(): any;
				new (): any;
				createUploadAdapter: (loader: any) => { upload(): Promise<unknown> };
			};
		};
	}) {
		editor.plugins.get('FileRepository').createUploadAdapter = (loader: any) => {
			return customUploadAdapter(loader);
		};
	}

	const customUploadAdapter = (loader: { file: Promise<any> }) => {
		setNum(num + 1);
		return {
			upload() {
				return new Promise((resolve, reject) => {
					const formData = new FormData();
					loader.file.then((file: string | Blob) => {
						formData.append('file', file);
						setFileList(prevList => [...prevList, file]);
						apiPostUploadFile(formData).then(res => {
							console.log(res.data);
							// showAlert('', t('com.msg.confirmSaved'), () => {});
							resolve({
								default: '' + res.data,
							});
						});
					});
				});
			},
		};
	};
	const saveData = (value: any) => {
		return new Promise<void>(resolve => {
			// Fake HTTP server's lag.
			setTimeout(() => {
				updateServerDataConsole(data);

				resolve();
			}, 500);
		});
	};
	/**
	 *
	 * @param msg
	 */
	let consoleUpdates = 0;
	/**
	 *
	 * @param msg
	 */
	function updateServerDataConsole(msg: string) {
		const console = document.querySelector('#snippet-autosave-console');

		consoleUpdates++;
		console.classList.add('updated');
		console.textContent = msg;

		setTimeout(() => {
			if (--consoleUpdates == 0) {
				console.classList.remove('updated');
			}
		}, 500);
	}

	const onClickPreview = () => {
		if (isPreview) {
			setIsPreview(false);
		} else {
			setIsPreview(true);
		}
	};

	// useEffect(() => {
	// 	if (editor) {
	// 		const toolbarElement = editor.ui.view.toolbar.element;
	// 		if (!isEdit) {
	// 			toolbarElement.style.display = 'none';
	// 		} else {
	// 			toolbarElement.style.display = 'flex';
	// 		}
	// 	}
	// }, [isEdit, editor]);

	return (
		<>
			{!isPreview ? (
				<CKEditor
					// ref={ref}
					editor={ClassicEditor}
					config={{
						plugins: [
							AutoLink,
							Autosave,
							FindAndReplace,
							// Base64UploadAdapter,
							Link,
							AutoLink,
							RemoveFormat,
							BlockQuote,
							Alignment,
							ListProperties,
							Strikethrough,
							Code,
							SourceEditing,
							HorizontalLine,
							Underline,
							MediaEmbed,
							Table,
							TableToolbar,
							Essentials,
							TableCellProperties,
							TableProperties,
							TableCaption,
							Paragraph,
							Bold,
							Italic,
							Heading,
							Font,
							SpecialCharacters,
							SpecialCharactersEssentials,
							Indent,
							SpecialCharacters,
							FontFamily,
							Image,
							ImageUpload,
							ImageInsertViaUrl,
							ImageInsert,
							Subscript,
							Superscript,
							ImageResizeEditing,
							ImageResizeHandles,
						],
						toolbar: {
							items: [
								'sourceEditing',
								'findAndReplace',
								'undo',
								'redo',
								'|',
								'heading',
								'|',
								'fontSize',
								'fontFamily',
								'fontColor',
								'fontBackgroundColor',
								'horizontalLine',
								'|',
								'bold',
								'italic',
								'alignment',
								'underline',
								'strikethrough',
								'subscript',
								'superscript',
								'removeFormat',
								'-',
								'bulletedList',
								'numberedList',
								'|',
								'link',
								offImage ? null : 'insertImage', // props로  onoff 가능
								'insertTable',
								'blockQuote',
								'mediaEmbed',
								'|',
								'outdent',
								'indent',
								'code',
								'specialCharacters',
							],
							shouldNotGroupWhenFull: true,
						},
						autosave: {
							waitingTime: 5000, // in ms
							save(editor: ClassicEditor) {
								// The saveData() function must return a promise
								// which should be resolved when the data is successfully saved.
								console.log(editor.getData());
								return saveData(editor.getData());
							},
						},
						list: {
							properties: {
								styles: true,
								startIndex: true,
								reversed: true,
							},
						},

						// head: {
						// 	options: [
						// 		{
						// 			model: 'paragraph',
						// 			view: 'p',
						// 			title: '본문',
						// 			class: 'ck-heading_paragraph',
						// 		},
						// 		{
						// 			model: 'heading1',
						// 			view: 'h1',
						// 			title: '헤더1',
						// 			class: 'ck-heading_heading1',
						// 		},
						// 		{
						// 			model: 'heading2',
						// 			view: 'h2',
						// 			title: '헤더2',
						// 			class: 'ck-heading_heading2',
						// 		},
						// 		{
						// 			model: 'heading3',
						// 			view: 'h3',
						// 			title: '헤더3',
						// 			class: 'ck-heading_heading3',
						// 		},
						// 	],
						// },
						image: {
							insert: {
								integrations: ['upload', 'url'],
								type: 'auto',
							},
							resizeOptions: [
								{
									name: 'resizeImage:original',
									value: null,
									label: 'Original',
								},
								{
									name: 'resizeImage:40',
									value: '40',
									label: '40%',
								},
								{
									name: 'resizeImage:60',
									value: '60',
									label: '60%',
								},
							],
							toolbar: ['resizeImage' /* ... */],
						},
						placeholder: '여기에 양식 데이터를 넣어주세요 ',
						language: 'ko',

						alignment: {
							options: ['justify', 'left', 'center', 'right'],
						},
						fontSize: {
							options: [9, 11, 13, 'default', 17, 19, 21],
						},
						fontFamily: {
							options: [
								'default',
								'궁서체',
								'바탕',
								'돋움',
								'Arial',
								'Courier New',
								'Georgia',
								'Lucida Sans Unicode',
								'Tahoma',
								'Times New Roman',
								'Trebuchet MS',
								'Verdana',
							],
							supportAllValues: true,
						},
						table: {
							contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', 'tableProperties', 'tableCellProperties'],
							tableProperties: {
								borderColors: customColorPalette,
								backgroundColors: customColorPalette,
							},
							tableCellProperties: {
								borderColors: customColorPalette,
								backgroundColors: customColorPalette,
							},
						},
						extraPlugins: [uploadPlugin],
					}}
					data={data}
					// onReady={neweditor => {
					// setEditor(neweditor);
					// }}
					onChange={(event, neweditor) => {
						setData(DOMPurify.sanitize(neweditor.getData()));
						if (setPlainText) {
							setPlainText(viewToPlainText(neweditor.editing.view.document.getRoot()).replace(/\n/g, ''));
						}
					}}
				/>
			) : (
				<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(data) }} />
			)}
			<Row>
				<Button type="primary" onClick={onClickPreview}>
					{!isPreview ? t('comfunc.bbs.search.preview') : t('comfunc.bbs.search.nopreview')}
				</Button>
			</Row>
		</>
	);
};

export default Ckeditor;


I followed the guide
but i have some error (Autosave)

i want to know how to using autosave function

Hi! Please provide the information about the error, or best, provide some reproducible sample for us (zip/repo). We not always have the time to set up the whole project and debug it.