Contribute to this guide

guide带有底部工具栏和按钮分组的编辑器

以下自定义编辑器示例展示了一个主工具栏显示在编辑窗口底部的编辑器实例。为了实现这一点,此示例使用 DecoupledEditor,并将 主工具栏 注入到编辑根节点后的 DOM 中。详细了解 CKEditor 5 中的解耦 UI,以了解此过程的详细信息。

此外,得益于 CKEditor 5 UI 框架 提供的灵活性,主工具栏已通过将与文本格式相关的按钮移动到自定义的“格式选项”下拉菜单中而变得简洁。所有剩余的下拉菜单和(按钮)工具提示都经过调整,以便向上打开,以获得最佳的用户体验。类似的效果也可以通过使用 内置的工具栏分组选项 来实现。

所呈现的 UI 和编辑器功能组合最适合文本创建优先,格式化偶尔应用的集成。一些例子是电子邮件应用程序、(论坛)帖子编辑器、聊天或即时消息。您可能从 Gmail、Slack 或 Zendesk 等流行应用程序中识别出此 UI 设置。

Fabulous Dummy App logo

欢迎来到 Fabulous Dummy App!


我们很高兴您加入我们,{user_name}!但在您探索所有功能之前,请验证您的电子邮件地址。

我们使用您的电子邮件地址验证您的帐户并及时为您提供最新信息。我们尊重您的隐私,您可以在您的帐户 偏好设置 中选择退出直接电子邮件营销。

如果您没有使用此电子邮件地址创建帐户,请联系我们 contact@fabulousdummyapp.com

验证您的帐户


Fabulous Dummy App
一个可以满足您所有需求的应用程序。

2776 Black Oak Hollow Road, San Jose, CA

# 编辑器示例配置

查看编辑器配置脚本

import {
    DecoupledEditor,
    Plugin,
    Alignment,
    Autoformat,
    Bold,
    Italic,
    Strikethrough,
    Subscript,
    Superscript,
    Underline,
    BlockQuote,
    clickOutsideHandler,
    Essentials,
    Font,
    Heading,
    HorizontalLine,
    Image,
    ImageCaption,
    ImageResize,
    ImageStyle,
    ImageToolbar,
    ImageUpload,
    Indent,
    Link,
    List,
    MediaEmbed,
    Paragraph,
    RemoveFormat,
    Table,
    TableToolbar,
    DropdownButtonView,
    DropdownPanelView,
    DropdownView,
    ToolbarView
} from 'ckeditor5';
import { EasyImage } from 'ckeditor5-premium-features';
import fontColorIcon from '@ckeditor/ckeditor5-font/theme/icons/font-color.svg';

class FormattingOptions extends Plugin {
    /**
    * @inheritDoc
    */
    static get pluginName() {
        return 'FormattingOptions';
    }

    /**
    * @inheritDoc
    */
    constructor( editor ) {
        super( editor );

        editor.ui.componentFactory.add( 'formattingOptions', locale => {
            const t = locale.t;
            const buttonView = new DropdownButtonView( locale );
            const panelView = new DropdownPanelView( locale );
            const dropdownView = new DropdownView( locale, buttonView, panelView );
            const toolbarView = this.toolbarView = dropdownView.toolbarView = new ToolbarView( locale );

            // Accessibility: Give the toolbar a human-readable ARIA label.
            toolbarView.set( {
                ariaLabel: t( 'Formatting options toolbar' )
            } );

            // Accessibility: Give the dropdown a human-readable ARIA label.
            dropdownView.set( {
                label: t( 'Formatting options' )
            } );

            // Toolbars in dropdowns need specific styling, hence the class.
            dropdownView.extendTemplate( {
                attributes: {
                    class: [ 'ck-toolbar-dropdown' ]
                }
            } );

            // Accessibility: If the dropdown panel is already open, the arrow down key should focus the first child of the #panelView.
            dropdownView.keystrokes.set( 'arrowdown', ( data, cancel ) => {
                if ( dropdownView.isOpen ) {
                    toolbarView.focus();
                    cancel();
                }
            } );

            // Accessibility: If the dropdown panel is already open, the arrow up key should focus the last child of the #panelView.
            dropdownView.keystrokes.set( 'arrowup', ( data, cancel ) => {
                if ( dropdownView.isOpen ) {
                    toolbarView.focusLast();
                    cancel();
                }
            } );

            // The formatting options should not close when the user clicked:
            // * the dropdown or it contents,
            // * any editing root,
            // * any floating UI in the "body" collection
            // It should close, for instance, when another (main) toolbar button was pressed, though.
            dropdownView.on( 'render', () => {
                clickOutsideHandler( {
                    emitter: dropdownView,
                    activator: () => dropdownView.isOpen,
                    callback: () => { dropdownView.isOpen = false; },
                    contextElements: [
                        dropdownView.element,
                        ...[ ...editor.ui.getEditableElementsNames() ].map( name => editor.ui.getEditableElement( name ) ),
                        document.querySelector( '.ck-body-wrapper' )
                    ]
                } );
            } );

            // The main button of the dropdown should be bound to the state of the dropdown.
            buttonView.bind( 'isOn' ).to( dropdownView, 'isOpen' );
            buttonView.bind( 'isEnabled' ).to( dropdownView );

            // Using the font color icon to visually represent the formatting.
            buttonView.set( {
                tooltip: t( 'Formatting options' ),
                icon: fontColorIcon
            } );

            dropdownView.panelView.children.add( toolbarView );

            toolbarView.fillFromConfig(
                editor.config.get( 'formattingOptions' ),
                editor.ui.componentFactory
            );

            return dropdownView;
        } );
    }
}

DecoupledEditor
    .create( document.querySelector( '#editor-content' ), {
        plugins: [
            Alignment,
            Autoformat,
            BlockQuote,
            Bold,
            EasyImage,
            Essentials,
            Font,
            Heading,
            HorizontalLine,
            Image,
            ImageCaption,
            ImageResize,
            ImageStyle,
            ImageToolbar,
            ImageUpload,
            Indent,
            Italic,
            Link,
            List,
            MediaEmbed,
            Paragraph,
            RemoveFormat,
            Strikethrough,
            Subscript,
            Superscript,
            Table,
            TableToolbar,
            Underline,

            FormattingOptions
        ],
        toolbar: [
            'undo',
            'redo',
            '|',
            'formattingOptions',
            '|',
            'link',
            'blockQuote',
            'uploadImage',
            'insertTable',
            'mediaEmbed',
            'horizontalLine',
            '|',
            {
                label: 'Lists',
                icon: false,
                items: [ 'bulletedList', 'numberedList', '|', 'outdent', 'indent' ]
            }
        ],
        // Configuration of the formatting dropdown.
        formattingOptions: [
            'undo',
            'redo',
            '|',
            'fontFamily',
            'fontSize',
            'fontColor',
            'fontBackgroundColor',
            '|',
            'bold',
            'italic',
            'underline',
            'strikethrough',
            '|',
            'alignment',
            '|',
            'bulletedList',
            'numberedList',
            '|',
            'outdent',
            'indent',
            '|',
            'removeFormat'
        ],

        image: {
            resizeUnit: 'px',
            toolbar: [
                'imageStyle:inline',
                'imageStyle:wrapText',
                'imageStyle:breakText',
                '|',
                'toggleImageCaption',
                'imageTextAlternative'
            ]
        },

        table: {
            contentToolbar: [
                'tableColumn',
                'tableRow',
                'mergeTableCells'
            ]
        },
        cloudServices: {
            // This editor configuration includes the Easy Image feature.
            // Provide correct configuration values to use it.
            tokenUrl: 'https://example.com/cs-token-endpoint',
            uploadUrl: 'https://your-organization-id.cke-cs.com/easyimage/upload/'
            // Read more about Easy Image - https://ckeditor.npmjs.net.cn/docs/ckeditor5/latest/features/images/image-upload/easy-image.html.
            // For other image upload methods see the guide - https://ckeditor.npmjs.net.cn/docs/ckeditor5/latest/features/images/image-upload/image-upload.html.
        },
    } )
    .then( editor => {
        window.editor = editor;

        const toolbarContainer = document.querySelector( '#editor-toolbar-container' );

        toolbarContainer.appendChild( editor.ui.view.toolbar.element );

        overrideDropdownPositionsToNorth( editor, editor.ui.view.toolbar );
        overrideDropdownPositionsToNorth( editor, editor.plugins.get( 'FormattingOptions' ).toolbarView );

        overrideTooltipPositions( editor.ui.view.toolbar );
        overrideTooltipPositions( editor.plugins.get( 'FormattingOptions' ).toolbarView );
    } )
    .catch( err => {
        console.error( err.stack );
    } );

/**
 * Force all toolbar dropdown panels to use northern positions rather than southern (editor default).
 * This will position them correctly relative to the toolbar at the bottom of the editing root.
 *
 * @private
 * @param {module:core/editor/editor~Editor} editor
 * @param {module:ui/toolbar/toolbarview~ToolbarView} toolbarView
 */
function overrideDropdownPositionsToNorth( editor, toolbarView ) {
    const {
        south, north, southEast, southWest, northEast, northWest,
        southMiddleEast, southMiddleWest, northMiddleEast, northMiddleWest
    } = DropdownView.defaultPanelPositions;

    let panelPositions;

    if ( editor.locale.uiLanguageDirection !== 'rtl' ) {
        panelPositions = [
            northEast, northWest, northMiddleEast, northMiddleWest, north,
            southEast, southWest, southMiddleEast, southMiddleWest, south
        ];
    } else {
        panelPositions = [
            northWest, northEast, northMiddleWest, northMiddleEast, north,
            southWest, southEast, southMiddleWest, southMiddleEast, south
        ];
    }

    for ( const item of toolbarView.items ) {
        if ( !( item instanceof DropdownView ) ) {
            continue;
        }

        item.on( 'change:isOpen', () => {
            if ( !item.isOpen ) {
                return;
            }

            item.panelView.position = DropdownView._getOptimalPosition( {
                element: item.panelView.element,
                target: item.buttonView.element,
                fitInViewport: true,
                positions: panelPositions
            } ).name;
        } );
    }
}

/**
 * Forces all toolbar items to display tooltips to the north.
 * This will position them correctly relative to the toolbar at the bottom of the editing root.
 *
 * @param {module:ui/toolbar/toolbarview~ToolbarView} toolbarView
 */
function overrideTooltipPositions( toolbarView ) {
    for ( const item of toolbarView.items ) {
        if ( item.buttonView ) {
            item.buttonView.tooltipPosition = 'n';
        } else if ( item.tooltipPosition ) {
            item.tooltipPosition = 'n';
        }
    }
}

查看编辑器内容列表
<style>
    #editor {
        display: flex;
        flex-direction: column;
    }

    #editor-content {
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
    }

    #editor-content:not(.ck-focused) {
        border-color: var(--ck-color-base-border);
    }

    #editor-toolbar-container > .ck.ck-toolbar {
        border-top-width: 0;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
    }

    .ck.ck-content {
        font-family: Helvetica, Arial, sans-serif;
        padding: 3em 2em 2em;
    }

    .ck.ck-content h2 {
        border: 0;
        font-size: 1.3em;
        padding-top: 0.2em;
        padding-bottom: 0.2em;
        margin-bottom: 0.4em;
    }

    .ck.ck-content .ck-horizontal-line.ck-widget {
        text-align: center;
    }

    .ck.ck-content .ck-horizontal-line.ck-widget hr {
        margin: 5px auto;
        width: 50px;
        height: 1px;
        display: inline-block;
    }
</style>

<div id="editor">
    <div id="editor-content">
            Editor content is inserted here.
    </div>
    <div id="editor-toolbar-container"></div>
</div>