diff --git a/package.json b/package.json index eaba9a1..2040562 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,10 @@ "@ckeditor/ckeditor5-editor-classic": "^12.1.0", "@ckeditor/ckeditor5-engine": "^13.1.0", "@ckeditor/ckeditor5-font": "^11.1.0", + "@ckeditor/ckeditor5-link": "^11.0.1", "@ckeditor/ckeditor5-paragraph": "^11.0.1", + "@ckeditor/ckeditor5-table": "^12.0.1", + "@ckeditor/ckeditor5-typing": "^12.0.1", "@ckeditor/ckeditor5-undo": "^11.0.1", "@ckeditor/ckeditor5-widget": "^11.0.1", "eslint": "^5.5.0", diff --git a/src/mentionui.js b/src/mentionui.js index 0a1ab9f..2bc4d91 100644 --- a/src/mentionui.js +++ b/src/mentionui.js @@ -296,6 +296,8 @@ export default class MentionUI extends Plugin { const nodeBefore = focus.nodeBefore; if ( hasMention || nodeBefore && nodeBefore.is( 'text' ) && nodeBefore.hasAttribute( 'mention' ) ) { + this._hideUIAndRemoveMarker(); + return; } @@ -387,12 +389,13 @@ export default class MentionUI extends Plugin { * @private */ _hideUIAndRemoveMarker() { - if ( this.editor.model.markers.has( 'mention' ) ) { - this.editor.model.change( writer => writer.removeMarker( 'mention' ) ); + // Remove the mention view from balloon before removing marker - it is used by balloon position target(). + if ( this._balloon.hasView( this._mentionsView ) ) { + this._balloon.remove( this._mentionsView ); } - if ( this._isUIVisible ) { - this._balloon.remove( this._mentionsView ); + if ( this.editor.model.markers.has( 'mention' ) ) { + this.editor.model.change( writer => writer.removeMarker( 'mention' ) ); } // Make the last matched position on panel view undefined so the #_getBalloonPanelPositionData() method will return all positions @@ -447,14 +450,22 @@ export default class MentionUI extends Plugin { * @private */ _getBalloonPanelPositionData( mentionMarker, preferredPosition ) { + const editor = this.editor; const editing = this.editor.editing; const domConverter = editing.view.domConverter; const mapper = editing.mapper; return { target: () => { - const viewRange = mapper.toViewRange( mentionMarker.getRange() ); + let modelRange = mentionMarker.getRange(); + + // Target the UI to the model selection range - the marker has been removed so probably the UI will not be shown anyway. + // The logic is used by ContextualBalloon to display another panel in the same place. + if ( modelRange.start.root.rootName == '$graveyard' ) { + modelRange = editor.model.document.selection.getFirstRange(); + } + const viewRange = mapper.toViewRange( modelRange ); const rangeRects = Rect.getDomRangeRects( domConverter.viewRangeToDom( viewRange ) ); return rangeRects.pop(); diff --git a/tests/mention-integration.js b/tests/mention-integration.js index e71933c..2091958 100644 --- a/tests/mention-integration.js +++ b/tests/mention-integration.js @@ -3,18 +3,25 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -/* global document */ +/* global document, setTimeout */ import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote'; import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; +import Table from '@ckeditor/ckeditor5-table/src/table'; +import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar'; import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting'; +import Link from '@ckeditor/ckeditor5-link/src/link'; +import Delete from '@ckeditor/ckeditor5-typing/src/delete'; import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; import { parse as parseView, getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view'; +import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; import MentionEditing from '../src/mentionediting'; +import Mention from '../src/mention'; +import MentionUI from '../src/mentionui'; describe( 'Mention feature - integration', () => { let div, editor, model, doc; @@ -208,4 +215,115 @@ describe( 'Mention feature - integration', () => { .to.equal( expectedData ); } ); } ); + + describe( 'with table toolbar', () => { + beforeEach( () => { + return ClassicTestEditor + .create( div, { + plugins: [ Paragraph, Table, TableToolbar, Mention ], + table: { + contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ] + }, + mention: { + feeds: [ + { + marker: '@', + feed: [ '@Barney', '@Lily', '@Marshall', '@Robin', '@Ted' ] + } + ] + } + } ) + .then( newEditor => { + editor = newEditor; + model = editor.model; + doc = model.document; + } ); + } ); + + it( 'should work with table toolbar', () => { + editor.ui.focusTracker.isFocused = true; + + setData( model, '