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

Commit

Permalink
Merge 67bfa89 into f3d1dee
Browse files Browse the repository at this point in the history
  • Loading branch information
oleq committed Mar 4, 2020
2 parents f3d1dee + 67bfa89 commit fb9b270
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 67 deletions.
90 changes: 31 additions & 59 deletions src/tableselection.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import TableUtils from './tableutils';
import { setupTableSelectionHighlighting } from './tableselection/converters';
import MouseSelectionHandler from './tableselection/mouseselectionhandler';
import { findAncestor } from './commands/utils';
import { clearTableCellsContents } from './tableselection/utils';
import { getTableCellsInSelection, clearTableCellsContents } from './tableselection/utils';
import cropTable from './tableselection/croptable';

import '../theme/tableselection.css';
Expand Down Expand Up @@ -107,29 +107,6 @@ export default class TableSelection extends Plugin {
this.listenTo( model, 'deleteContent', ( evt, args ) => this._handleDeleteContent( evt, args ), { priority: 'high' } );
}

/**
* @inheritDoc
*/
afterInit() {
const editor = this.editor;

const deleteCommand = editor.commands.get( 'delete' );

if ( deleteCommand ) {
this.listenTo( deleteCommand, 'execute', event => {
this._handleDeleteCommand( event, { isForward: false } );
}, { priority: 'high' } );
}

const forwardDeleteCommand = editor.commands.get( 'forwardDelete' );

if ( forwardDeleteCommand ) {
this.listenTo( forwardDeleteCommand, 'execute', event => {
this._handleDeleteCommand( event, { isForward: true } );
}, { priority: 'high' } );
}
}

/**
* @inheritDoc
*/
Expand Down Expand Up @@ -315,41 +292,36 @@ export default class TableSelection extends Plugin {
* @param {Array.<*>} args Delete content method arguments.
*/
_handleDeleteContent( event, args ) {
const [ selection ] = args;
const model = this.editor.model;

if ( this.hasMultiCellSelection && selection.is( 'documentSelection' ) ) {
event.stop();

clearTableCellsContents( model, this.getSelectedTableCells() );

model.change( writer => {
writer.setSelection( Array.from( this.getSelectedTableCells() ).pop(), 0 );
} );
}
}

/**
* It overrides default `DeleteCommand` behavior over a selected table fragment.
*
* @private
* @param {module:utils/eventinfo~EventInfo} event
* @param {Object} options
* @param {Boolean} options.isForward Whether it handles forward or backward delete.
*/
_handleDeleteCommand( event, options ) {
const model = this.editor.model;

if ( this.hasMultiCellSelection ) {
event.stop();

clearTableCellsContents( model, this.getSelectedTableCells() );

const tableCell = options.isForward ? this._startElement : this._endElement;

model.change( writer => {
writer.setSelection( tableCell, 0 );
} );
const [ selection, options ] = args;

// This Model#deleteContent() hook supports multiple-cell selections only.
// **Note**: It executes Model#deleteContent() itself for each individual cell within
// the selection, so this also avoids infinite loops.
if ( selection.rangeCount > 1 ) {
const model = this.editor.model;
const isBackward = !options || options.direction == 'backward';
const selectedTableCells = getTableCellsInSelection( selection );

// There are possible multiple-range selection that have nothing to do with tables.
// Let's filter them out and focus on table cells only.
if ( selectedTableCells.length ) {
event.stop();

model.change( writer => {
const tableCellToSelect = selectedTableCells[ isBackward ? selectedTableCells.length - 1 : 0 ];

clearTableCellsContents( model, selectedTableCells );

// The insertContent() helper passes the actual DocumentSelection,
// while the deleteContent() helper always operates on the abstract clones.
if ( selection.is( 'documentSelection' ) ) {
writer.setSelection( tableCellToSelect, 'in' );
} else {
selection.setTo( tableCellToSelect, 'in' );
}
} );
}
}
}
}

22 changes: 20 additions & 2 deletions src/tableselection/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
*
* clearTableCellsContents( editor.model, tableSelection.getSelectedTableCells() );
*
* **Note**: This function is used also by {@link module:table/tableselection~TableSelection#getSelectionAsFragment}
*
* @param {module:engine/model/model~Model} model
* @param {Iterable.<module:engine/model/element~Element>} tableCells
*/
Expand All @@ -29,3 +27,23 @@ export function clearTableCellsContents( model, tableCells ) {
}
} );
}

/**
* Returns all model cells within the provided model selection.
*
* @param {Iterable.<module:engine/model/selection~Selection>} selection
* @returns {Array.<module:engine/model/element~Element>}
*/
export function getTableCellsInSelection( selection ) {
const cells = [];

for ( const range of selection.getRanges() ) {
for ( const item of range.getItems() ) {
if ( item.is( 'tableCell' ) ) {
cells.push( item );
}
}
}

return cells;
}
64 changes: 58 additions & 6 deletions tests/tableselection-integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,56 @@ describe( 'table selection', () => {
[ '31', '32', '33' ]
] ) );
} );

it( 'should work with any arbitrary selection passed to Model#deleteContent() (delete backwards)', () => {
const selection = model.createSelection( [
model.createRange(
model.createPositionFromPath( modelRoot, [ 0, 0, 0 ] ),
model.createPositionFromPath( modelRoot, [ 0, 0, 1 ] )
),
model.createRange(
model.createPositionFromPath( modelRoot, [ 0, 0, 1 ] ),
model.createPositionFromPath( modelRoot, [ 0, 0, 2 ] )
)
] );

model.change( writer => {
model.deleteContent( selection );
writer.setSelection( selection );
} );

assertEqualMarkup( getModelData( model ), modelTable( [
[ '', '[]', '13' ],
[ '21', '22', '23' ],
[ '31', '32', '33' ]
] ) );
} );

it( 'should work with any arbitrary selection passed to Model#deleteContent() (delete forwards)', () => {
const selection = model.createSelection( [
model.createRange(
model.createPositionFromPath( modelRoot, [ 0, 0, 0 ] ),
model.createPositionFromPath( modelRoot, [ 0, 0, 1 ] )
),
model.createRange(
model.createPositionFromPath( modelRoot, [ 0, 0, 1 ] ),
model.createPositionFromPath( modelRoot, [ 0, 0, 2 ] )
)
] );

model.change( writer => {
model.deleteContent( selection, {
direction: 'forward'
} );
writer.setSelection( selection );
} );

assertEqualMarkup( getModelData( model ), modelTable( [
[ '[]', '', '13' ],
[ '21', '22', '23' ],
[ '31', '32', '33' ]
] ) );
} );
} );

describe( 'on user input', () => {
Expand All @@ -108,15 +158,16 @@ describe( 'table selection', () => {

viewDocument.fire( 'keydown', { keyCode: getCode( 'x' ) } );

// figure table tbody tr td span
const viewSpan = viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 1 ).getChild( 1 ).getChild( 0 );
// Mutate at the place where the document selection was put; it's more realistic
// than mutating at some arbitrary position.
const placeOfMutation = viewDocument.selection.getFirstRange().start.parent;

viewDocument.fire( 'mutations', [
{
type: 'children',
oldChildren: [],
newChildren: [ new ViewText( viewDocument, 'x' ) ],
node: viewSpan
node: placeOfMutation
}
] );

Expand All @@ -130,15 +181,16 @@ describe( 'table selection', () => {
it( 'should not interfere with default key handler if no table selection', () => {
viewDocument.fire( 'keydown', { keyCode: getCode( 'x' ) } );

// figure table tbody tr td span
const viewSpan = viewDocument.getRoot().getChild( 0 ).getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 ).getChild( 0 );
// Mutate at the place where the document selection was put; it's more realistic
// than mutating at some arbitrary position.
const placeOfMutation = viewDocument.selection.getFirstRange().start.parent;

viewDocument.fire( 'mutations', [
{
type: 'children',
oldChildren: [],
newChildren: [ new ViewText( viewDocument, 'x' ) ],
node: viewSpan
node: placeOfMutation
}
] );

Expand Down

0 comments on commit fb9b270

Please sign in to comment.