Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #24 from ckeditor/t/23
Browse files Browse the repository at this point in the history
Fix: Correctly handling select all keystroke when widget is selected. Closes #23.
  • Loading branch information
oskarwrobel authored Oct 23, 2017
2 parents 1dfdc83 + cb75d73 commit 3e8f91f
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 15 deletions.
80 changes: 66 additions & 14 deletions src/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,38 @@ export default class Widget extends Plugin {
init() {
const viewDocument = this.editor.editing.view;

let previouslySelected;
/**
* Holds previously selected widgets.
*
* @private
* @type {Set.<module:engine/view/element~Element>}
*/
this._previouslySelected = new Set();

// Model to view selection converter.
// Converts selection placed over widget element to fake selection
this.editor.editing.modelToView.on( 'selection', ( evt, data, consumable, conversionApi ) => {
// Remove selected class from previously selected widget.
if ( previouslySelected && previouslySelected.hasClass( WIDGET_SELECTED_CLASS_NAME ) ) {
previouslySelected.removeClass( WIDGET_SELECTED_CLASS_NAME );
}
// Remove selected class from previously selected widgets.
this._clearPreviouslySelectedWidgets();

const viewSelection = conversionApi.viewSelection;

// Check if widget was clicked or some sub-element.
const selectedElement = viewSelection.getSelectedElement();

if ( !selectedElement || !isWidget( selectedElement ) ) {
return;
}
for ( const range of viewSelection.getRanges() ) {
for ( const value of range ) {
const node = value.item;

viewSelection.setFake( true, { label: getLabel( selectedElement ) } );
selectedElement.addClass( WIDGET_SELECTED_CLASS_NAME );
previouslySelected = selectedElement;
if ( node.is( 'element' ) && isWidget( node ) ) {
node.addClass( WIDGET_SELECTED_CLASS_NAME );
this._previouslySelected.add( node );

// Check if widget is a single element selected.
if ( node == selectedElement ) {
viewSelection.setFake( true, { label: getLabel( selectedElement ) } );
}
}
}
}
}, { priority: 'low' } );

// If mouse down is pressed on widget - create selection over whole widget.
Expand Down Expand Up @@ -136,7 +146,7 @@ export default class Widget extends Plugin {
} else if ( isArrowKeyCode( keyCode ) ) {
wasHandled = this._handleArrowKeys( isForward );
} else if ( isSelectAllKeyCode( domEventData ) ) {
wasHandled = this._selectAllNestedEditableContent();
wasHandled = this._selectAllNestedEditableContent() || this._selectAllContent();
}

if ( wasHandled ) {
Expand Down Expand Up @@ -256,6 +266,36 @@ export default class Widget extends Plugin {
return true;
}

/**
* Handles <kbd>CTRL + A</kbd> when widget is selected.
*
* @private
* @returns {Boolean} Returns true if widget was selected and selecting all was handled by this method.
*/
_selectAllContent() {
const modelDocument = this.editor.document;
const modelSelection = modelDocument.selection;
const editing = this.editor.editing;
const viewDocument = editing.view;
const viewSelection = viewDocument.selection;

const selectedElement = viewSelection.getSelectedElement();

// Only widget is selected.
// https://github.com/ckeditor/ckeditor5-widget/issues/23
if ( selectedElement && isWidget( selectedElement ) ) {
const widgetParent = editing.mapper.toModelElement( selectedElement.parent );

modelDocument.enqueueChanges( () => {
modelSelection.setRanges( [ ModelRange.createIn( widgetParent ) ] );
} );

return true;
}

return false;
}

/**
* Sets {@link module:engine/model/selection~Selection document's selection} over given element.
*
Expand Down Expand Up @@ -293,6 +333,18 @@ export default class Widget extends Plugin {

return null;
}

/**
* Removes CSS class from previously selected widgets.
* @private
*/
_clearPreviouslySelectedWidgets() {
for ( const widget of this._previouslySelected ) {
widget.removeClass( WIDGET_SELECTED_CLASS_NAME );
}

this._previouslySelected.clear();
}
}

// Returns 'true' if provided key code represents one of the arrow keys.
Expand Down
28 changes: 27 additions & 1 deletion tests/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,19 @@ describe( 'Widget', () => {
expect( viewDocument.selection.fakeSelectionLabel ).to.equal( 'element label' );
} );

it( 'should add selected class when no only a widget is selected', () => {
setModelData( doc, '[<paragraph>foo</paragraph><widget></widget><widget></widget>]' );

expect( viewDocument.selection.isFake ).to.be.false;
expect( getViewData( viewDocument ) ).to.equal(
'[' +
'<p>foo</p>' +
'<div class="ck-widget ck-widget_selected" contenteditable="false"><b></b></div>' +
'<div class="ck-widget ck-widget_selected" contenteditable="false"><b></b></div>' +
']'
);
} );

it( 'fake selection should be empty if widget is not selected', () => {
setModelData( doc, '<paragraph>foo</paragraph><widget>foo bar</widget>' );

Expand Down Expand Up @@ -1075,9 +1088,18 @@ describe( 'Widget', () => {
{ keyCode: keyCodes.a, ctrlKey: true },
'<widget><nested>foo</nested></widget><paragraph>[]bar</paragraph>'
);

test(
'should selected whole content when widget is selected',
'<paragraph>foo</paragraph>[<widget></widget>]<paragraph>bar</paragraph>',
{ keyCode: keyCodes.a, ctrlKey: true },
'[<paragraph>foo</paragraph><widget></widget><paragraph>bar</paragraph>]',
'[<p>foo</p><div class="ck-widget ck-widget_selected" contenteditable="false"><b></b></div><p>bar</p>]'

);
} );

function test( name, data, keyCodeOrMock, expected ) {
function test( name, data, keyCodeOrMock, expected, expectedView ) {
it( name, () => {
const domEventDataMock = ( typeof keyCodeOrMock == 'object' ) ? keyCodeOrMock : {
keyCode: keyCodeOrMock
Expand All @@ -1091,6 +1113,10 @@ describe( 'Widget', () => {
) );

expect( getModelData( doc ) ).to.equal( expected );

if ( expectedView ) {
expect( getViewData( viewDocument ) ).to.equal( expectedView );
}
} );
}
} );
Expand Down

0 comments on commit 3e8f91f

Please sign in to comment.