ckeditor / ckeditor5-react

Official CKEditor 5 React component.
https://ckeditor.com/ckeditor-5
Other
424 stars 99 forks source link

how to install autosave in React, TypeScript project #454

Open surzioarmani opened 8 months ago

surzioarmani commented 8 months ago
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

Witoso commented 8 months ago

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.