Skip to content

Commit

Permalink
Merge pull request #7237 from ckeditor/i/6775
Browse files Browse the repository at this point in the history
Internal (widget): Integrated the `WidgetTypeAround` plugin with the read–only mode. Closes #6775.

Internal (widget): Integrated the `WidgetTypeAround` plugin with the `RestrictedEditingMode` plugin (see #6775).

Feature (paragraph): Implemented the `InsertParagraphCommand` registered as `'insertParagraph'` in the editor. Closes #6823. Closes #7229.

Docs (engine): Improved `Model#insertContent` API documentation (see #6775).
  • Loading branch information
niegowski committed May 21, 2020
2 parents 3cb1c78 + f4ae9be commit 1267018
Show file tree
Hide file tree
Showing 12 changed files with 295 additions and 20 deletions.
21 changes: 18 additions & 3 deletions packages/ckeditor5-engine/src/model/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,13 +370,28 @@ export default class Model {
* editor.model.insertContent( writer.createText( 'x' ), writer.createPositionAt( doc.getRoot(), 2 ) );
* } );
*
* If an instance of {@link module:engine/model/selection~Selection} is passed as `selectable`
* it will be moved to the target position (where the document selection should be moved after the insertion).
* If you want the document selection to be moved to the inserted content, use the
* {@link module:engine/model/writer~Writer#setSelection `setSelection()`} method of the writer after inserting
* the content:
*
* editor.model.change( writer => {
* // Insert text replacing the given selection instance.
* const paragraph = writer.createElement( 'paragraph' );
*
* // Insert an empty paragraph at the beginning of the root.
* editor.model.insertContent( paragraph, writer.createPositionAt( editor.model.document.getRoot(), 0 ) );
*
* // Move the document selection to the inserted paragraph.
* writer.setSelection( paragraph, 'in' );
* } );
*
* If an instance of the {@link module:engine/model/selection~Selection model selection} is passed as `selectable`,
* the new content will be inserted at the passed selection (instead of document selection):
*
* editor.model.change( writer => {
* // Create a selection in a paragraph that will be used as a place of insertion.
* const selection = writer.createSelection( paragraph, 'in' );
*
* // Insert the new text at the created selection.
* editor.model.insertContent( writer.createText( 'x' ), selection );
*
* // insertContent() modifies the passed selection instance so it can be used to set the document selection.
Expand Down
1 change: 0 additions & 1 deletion packages/ckeditor5-highlight/src/highlightcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export default class HighlightCommand extends Command {
/**
* Executes the command.
*
* @protected
* @param {Object} [options] Options for the executed command.
* @param {String} [options.value] The value to apply.
*
Expand Down
49 changes: 49 additions & 0 deletions packages/ckeditor5-paragraph/src/insertparagraphcommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module paragraph/insertparagraphcommand
*/

import Command from '@ckeditor/ckeditor5-core/src/command';

/**
* The insert paragraph command. It inserts a new paragraph at a specific
* {@link module:engine/model/position~Position document position}.
*
* // Insert a new paragraph before an element in the document.
* editor.execute( 'insertParagraph', {
* position: editor.model.createPositionBefore( element )
* } );
*
* **Note**: This command moves the selection to the inserted paragraph.
*
* @extends module:core/command~Command
*/
export default class InsertParagraphCommand extends Command {
/**
* Executes the command.
*
* @param {Object} options Options for the executed command.
* @param {module:engine/model/position~Position} options.position The model position at which
* the new paragraph will be inserted.
* @fires execute
*/
execute( options ) {
const model = this.editor.model;

if ( !model.schema.checkChild( options.position, 'paragraph' ) ) {
return;
}

model.change( writer => {
const paragraph = writer.createElement( 'paragraph' );

model.insertContent( paragraph, options.position );

writer.setSelection( paragraph, 'in' );
} );
}
}
9 changes: 9 additions & 0 deletions packages/ckeditor5-paragraph/src/paragraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import ParagraphCommand from './paragraphcommand';
import InsertParagraphCommand from './insertparagraphcommand';

import Plugin from '@ckeditor/ckeditor5-core/src/plugin';

Expand All @@ -16,6 +17,13 @@ import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
*
* It introduces the `<paragraph>` element in the model which renders as a `<p>` element in the DOM and data.
*
* It also brings two editors commands:
*
* * The {@link module:paragraph/paragraphcommand~ParagraphCommand `'paragraph'`} command that converts all
* blocks in the model selection into paragraphs.
* * The {@link module:paragraph/insertparagraphcommand~InsertParagraphCommand `'insertParagraph'`} command
* that inserts a new paragraph at a specified location in the model.
*
* @extends module:core/plugin~Plugin
*/
export default class Paragraph extends Plugin {
Expand All @@ -35,6 +43,7 @@ export default class Paragraph extends Plugin {
const data = editor.data;

editor.commands.add( 'paragraph', new ParagraphCommand( editor ) );
editor.commands.add( 'insertParagraph', new InsertParagraphCommand( editor ) );

// Schema.
model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
Expand Down
101 changes: 101 additions & 0 deletions packages/ckeditor5-paragraph/tests/insertparagraphcommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
import InsertParagraphCommand from '../src/insertparagraphcommand';

import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';

describe( 'InsertParagraphCommand', () => {
let editor, model, document, command, root, schema;

beforeEach( () => {
return ModelTestEditor.create().then( newEditor => {
editor = newEditor;
model = editor.model;
document = model.document;
schema = model.schema;
command = new InsertParagraphCommand( editor );
root = document.getRoot();

editor.commands.add( 'insertParagraph', command );
schema.register( 'paragraph', { inheritAllFrom: '$block' } );
schema.register( 'heading1', { inheritAllFrom: '$block', allowIn: 'headersOnly' } );
schema.register( 'headersOnly', { inheritAllFrom: '$block' } );
} );
} );

afterEach( () => {
command.destroy();
} );

describe( 'execute()', () => {
it( 'should insert a paragraph at a specific document position and anchor the selection inside of it', () => {
setData( model, '<heading1>foo[]</heading1>' );

command.execute( {
position: model.createPositionBefore( root.getChild( 0 ) )
} );

expect( getData( model ) ).to.equal( '<paragraph>[]</paragraph><heading1>foo</heading1>' );
} );

it( 'should do nothing if the paragraph is not allowed at the provided position', () => {
setData( model, '<headersOnly><heading1>foo[]</heading1></headersOnly>' );

command.execute( {
position: model.createPositionBefore( root.getChild( 0 ).getChild( 0 ) )
} );

command.execute( {
position: model.createPositionAfter( root.getChild( 0 ).getChild( 0 ) )
} );

expect( getData( model ) ).to.equal( '<headersOnly><heading1>foo[]</heading1></headersOnly>' );
} );

describe( 'interation with existing paragraphs in the content', () => {
it( 'should insert a paragraph before another paragraph', () => {
setData( model, '<paragraph>foo[]</paragraph>' );

command.execute( {
position: model.createPositionBefore( root.getChild( 0 ) )
} );

expect( getData( model ) ).to.equal( '<paragraph>[]</paragraph><paragraph>foo</paragraph>' );
} );

it( 'should insert a paragraph after another paragraph', () => {
setData( model, '<paragraph>foo[]</paragraph>' );

command.execute( {
position: model.createPositionAfter( root.getChild( 0 ) )
} );

expect( getData( model ) ).to.equal( '<paragraph>foo</paragraph><paragraph>[]</paragraph>' );
} );

it( 'should not merge with a paragraph that precedes the position at which a new paragraph is inserted', () => {
setData( model, '<paragraph>bar</paragraph><heading1>foo[]</heading1>' );

command.execute( {
position: model.createPositionBefore( root.getChild( 1 ) )
} );

expect( getData( model ) ).to.equal( '<paragraph>bar</paragraph><paragraph>[]</paragraph><heading1>foo</heading1>' );
} );

it( 'should not merge with a paragraph that follows the position at which a new paragraph is inserted', () => {
setData( model, '<heading1>foo[]</heading1><paragraph>bar</paragraph>' );

command.execute( {
position: model.createPositionAfter( root.getChild( 0 ) )
} );

expect( getData( model ) ).to.equal( '<heading1>foo</heading1><paragraph>[]</paragraph><paragraph>bar</paragraph>' );
} );
} );
} );
} );
9 changes: 7 additions & 2 deletions packages/ckeditor5-paragraph/tests/paragraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import Paragraph from '../src/paragraph';
import ParagraphCommand from '../src/paragraphcommand';
import InsertParagraphCommand from '../src/insertparagraphcommand';
import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
import {
getData as getModelData,
Expand Down Expand Up @@ -437,9 +438,13 @@ describe( 'Paragraph feature', () => {
} );
} );

describe( 'command', () => {
it( 'should be set in the editor', () => {
describe( 'commands', () => {
it( '"paragraph" command should be registered in the editor', () => {
expect( editor.commands.get( 'paragraph' ) ).to.be.instanceof( ParagraphCommand );
} );

it( '"insertParagraph" command should be registered in the editor', () => {
expect( editor.commands.get( 'insertParagraph' ) ).to.be.instanceof( InsertParagraphCommand );
} );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,12 @@

&:not(.ck-widget_selected) {
/* Disable visual effects of hover/active widget when CKEditor is in readOnly mode.
See: https://github.com/ckeditor/ckeditor5/issues/1261 */
--ck-widget-outline-thickness: 0;
* See: https://github.com/ckeditor/ckeditor5/issues/1261
*
* Leave the unit because this custom property is used in calc() by other features.
* See: https://github.com/ckeditor/ckeditor5/issues/6775
*/
--ck-widget-outline-thickness: 0px;
}

&.ck-widget_with-selection-handle {
Expand Down
47 changes: 37 additions & 10 deletions packages/ckeditor5-widget/src/widgettypearound/widgettypearound.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ export default class WidgetTypeAround extends Plugin {
this._enableTypeAroundUIInjection();
this._enableDetectionOfTypeAroundWidgets();
this._enableInsertingParagraphsOnButtonClick();

// TODO: This is a quick fix and it should be removed the proper integration arrives.
this._enableTemporaryTrackChangesIntegration();
}

/**
Expand All @@ -105,21 +108,17 @@ export default class WidgetTypeAround extends Plugin {
_insertParagraph( widgetViewElement, position ) {
const editor = this.editor;
const editingView = editor.editing.view;
let viewPosition;
const widgetModelElement = editor.editing.mapper.toModelElement( widgetViewElement );
let modelPosition;

if ( position === 'before' ) {
viewPosition = editingView.createPositionBefore( widgetViewElement );
modelPosition = editor.model.createPositionBefore( widgetModelElement );
} else {
viewPosition = editingView.createPositionAfter( widgetViewElement );
modelPosition = editor.model.createPositionAfter( widgetModelElement );
}

const modelPosition = editor.editing.mapper.toModelPosition( viewPosition );

editor.model.change( writer => {
const paragraph = writer.createElement( 'paragraph' );

writer.insert( paragraph, modelPosition );
writer.setSelection( paragraph, 0 );
editor.execute( 'insertParagraph', {
position: modelPosition
} );

editingView.focus();
Expand Down Expand Up @@ -222,6 +221,34 @@ export default class WidgetTypeAround extends Plugin {
evt.stop();
} );
}

/**
* A quick fix for the integration with features requiring custom handling of the `insertParagraph`
* command such as Track Changes. When the `insertParagraph` command is disabled, this fix adds
* a CSS class to editor roots that makes the UI disappear.
*
* TODO: This is a quick fix and it should be replaced by a proper integration.
*
* @private
*/
_enableTemporaryTrackChangesIntegration() {
const editor = this.editor;
const editingView = editor.editing.view;
const insertParagraphCommand = this.editor.commands.get( 'insertParagraph' );
const className = 'ck-widget_type-around_temp-disabled';

this.listenTo( insertParagraphCommand, 'change:isEnabled', ( evt, name, isEnabled ) => {
editingView.change( writer => {
for ( const root of editingView.document.roots ) {
if ( isEnabled ) {
writer.removeClass( className, root );
} else {
writer.addClass( className, root );
}
}
} );
} );
}
}

// Injects the type around UI into a view widget instance.
Expand Down
2 changes: 2 additions & 0 deletions packages/ckeditor5-widget/tests/manual/type-around.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
}
</style>
</head>
<button id="toggleReadOnly" type="button">Toggle read–only mode</button>
<hr />
<div id="editor">
<figure class="image"><img src="sample.jpg" alt="bar">
<figcaption>Caption</figcaption>
Expand Down
4 changes: 4 additions & 0 deletions packages/ckeditor5-widget/tests/manual/type-around.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ class InlineWidget extends Plugin {
}
}

document.querySelector( '#toggleReadOnly' ).addEventListener( 'click', () => {
window.editor.isReadOnly = !window.editor.isReadOnly;
} );

ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [ ArticlePluginSet, HorizontalLine, InlineWidget, MediaEmbed ],
Expand Down
Loading

0 comments on commit 1267018

Please sign in to comment.