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 #304 from ckeditor/i/3267
Browse files Browse the repository at this point in the history
Feature: Introduce custom keyboard navigation through tables. Closes ckeditor/ckeditor5#3267. Closes ckeditor/ckeditor5#3286.
  • Loading branch information
jodator authored Apr 23, 2020
2 parents 15016bf + 92d1567 commit d480c6d
Show file tree
Hide file tree
Showing 6 changed files with 2,806 additions and 437 deletions.
4 changes: 3 additions & 1 deletion src/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import TableEditing from './tableediting';
import TableUI from './tableui';
import TableSelection from './tableselection';
import TableClipboard from './tableclipboard';
import TableNavigation from './tablenavigation';
import Widget from '@ckeditor/ckeditor5-widget/src/widget';

import '../theme/table.css';
Expand All @@ -26,6 +27,7 @@ import '../theme/table.css';
*
* * {@link module:table/tableediting~TableEditing editing feature},
* * {@link module:table/tableselection~TableSelection selection feature},
* * {@link module:table/tablenavigation~TableNavigation keyboard navigation feature},
* * {@link module:table/tableclipboard~TableClipboard clipboard feature},
* * {@link module:table/tableui~TableUI UI feature}.
*
Expand All @@ -36,7 +38,7 @@ export default class Table extends Plugin {
* @inheritDoc
*/
static get requires() {
return [ TableEditing, TableUI, TableSelection, TableClipboard, Widget ];
return [ TableEditing, TableUI, TableSelection, TableClipboard, TableNavigation, Widget ];
}

/**
Expand Down
104 changes: 0 additions & 104 deletions src/tableediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import SetHeaderColumnCommand from './commands/setheadercolumncommand';
import MergeCellsCommand from './commands/mergecellscommand';
import SelectRowCommand from './commands/selectrowcommand';
import SelectColumnCommand from './commands/selectcolumncommand';
import { getTableCellsContainingSelection } from './utils';
import TableUtils from '../src/tableutils';

import injectTableLayoutPostFixer from './converters/table-layout-post-fixer';
Expand Down Expand Up @@ -150,11 +149,6 @@ export default class TableEditing extends Plugin {
injectTableLayoutPostFixer( model );
injectTableCellRefreshPostFixer( model );
injectTableCellParagraphPostFixer( model );

// Handle Tab key navigation.
this.editor.keystrokes.set( 'Tab', ( ...args ) => this._handleTabOnSelectedTable( ...args ), { priority: 'low' } );
this.editor.keystrokes.set( 'Tab', this._getTabHandler( true ), { priority: 'low' } );
this.editor.keystrokes.set( 'Shift+Tab', this._getTabHandler( false ), { priority: 'low' } );
}

/**
Expand All @@ -163,102 +157,4 @@ export default class TableEditing extends Plugin {
static get requires() {
return [ TableUtils ];
}

/**
* Handles {@link module:engine/view/document~Document#event:keydown keydown} events for the <kbd>Tab</kbd> key executed
* when the table widget is selected.
*
* @private
* @param {module:utils/eventinfo~EventInfo} eventInfo
* @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
*/
_handleTabOnSelectedTable( domEventData, cancel ) {
const editor = this.editor;
const selection = editor.model.document.selection;

if ( !selection.isCollapsed && selection.rangeCount === 1 && selection.getFirstRange().isFlat ) {
const selectedElement = selection.getSelectedElement();

if ( !selectedElement || !selectedElement.is( 'table' ) ) {
return;
}

cancel();

editor.model.change( writer => {
writer.setSelection( writer.createRangeIn( selectedElement.getChild( 0 ).getChild( 0 ) ) );
} );
}
}

/**
* Returns a handler for {@link module:engine/view/document~Document#event:keydown keydown} events for the <kbd>Tab</kbd> key executed
* inside table cell.
*
* @private
* @param {Boolean} isForward Whether this handler will move the selection to the next or the previous cell.
*/
_getTabHandler( isForward ) {
const editor = this.editor;

return ( domEventData, cancel ) => {
const selection = editor.model.document.selection;
const tableCell = getTableCellsContainingSelection( selection )[ 0 ];

if ( !tableCell ) {
return;
}

cancel();

const tableRow = tableCell.parent;
const table = tableRow.parent;

const currentRowIndex = table.getChildIndex( tableRow );
const currentCellIndex = tableRow.getChildIndex( tableCell );

const isFirstCellInRow = currentCellIndex === 0;

if ( !isForward && isFirstCellInRow && currentRowIndex === 0 ) {
// It's the first cell of the table - don't do anything (stay in the current position).
return;
}

const isLastCellInRow = currentCellIndex === tableRow.childCount - 1;
const isLastRow = currentRowIndex === table.childCount - 1;

if ( isForward && isLastRow && isLastCellInRow ) {
editor.execute( 'insertTableRowBelow' );

// Check if the command actually added a row. If `insertTableRowBelow` execution didn't add a row (because it was disabled
// or it got overwritten) do not change the selection.
if ( currentRowIndex === table.childCount - 1 ) {
return;
}
}

let cellToFocus;

// Move to first cell in next row.
if ( isForward && isLastCellInRow ) {
const nextRow = table.getChild( currentRowIndex + 1 );

cellToFocus = nextRow.getChild( 0 );
}
// Move to last cell in a previous row.
else if ( !isForward && isFirstCellInRow ) {
const previousRow = table.getChild( currentRowIndex - 1 );

cellToFocus = previousRow.getChild( previousRow.childCount - 1 );
}
// Move to next/previous cell.
else {
cellToFocus = tableRow.getChild( currentCellIndex + ( isForward ? 1 : -1 ) );
}

editor.model.change( writer => {
writer.setSelection( writer.createRangeIn( cellToFocus ) );
} );
};
}
}
Loading

0 comments on commit d480c6d

Please sign in to comment.