@ckeditor/ckeditor5-table
Advanced tools
Comparing version 10.0.0 to 10.1.0
Changelog | ||
========= | ||
## [10.1.0](https://github.com/ckeditor/ckeditor5-table/compare/v10.0.0...v10.1.0) (2018-07-18) | ||
### Features | ||
* Implemented the table selection handle (see [ckeditor/ckeditor5-widget#40](https://github.com/ckeditor/ckeditor5-widget/issues/40)). ([47295bd](https://github.com/ckeditor/ckeditor5-table/commit/47295bd)) | ||
* Used the switch button to toggle table headers (see [ckeditor/ckeditor5-ui#402](https://github.com/ckeditor/ckeditor5-ui/issues/402)). ([f3b7d0b](https://github.com/ckeditor/ckeditor5-table/commit/f3b7d0b)) | ||
Also: | ||
* Aligned the `TableUI` to the new API of the `addListToDropdown()` helper, | ||
* Updated the tests to consider the `ListItemView` as simply a container for buttons. | ||
### Bug fixes | ||
* Merge cell horizontally should not be possible on overlapped cells. Closes [#68](https://github.com/ckeditor/ckeditor5-table/issues/68). ([72b6315](https://github.com/ckeditor/ckeditor5-table/commit/72b6315)) | ||
* The `MergeCellCommand` should check if merging cells results in an empty row and remove it. Closes [#16](https://github.com/ckeditor/ckeditor5-table/issues/16). ([a01252a](https://github.com/ckeditor/ckeditor5-table/commit/a01252a)) | ||
### Other changes | ||
* Updated translations. ([1730b88](https://github.com/ckeditor/ckeditor5-table/commit/1730b88)) | ||
## 10.0.0 (2018-06-21) | ||
@@ -5,0 +26,0 @@ |
{ | ||
"name": "@ckeditor/ckeditor5-table", | ||
"version": "10.0.0", | ||
"version": "10.1.0", | ||
"description": "Table feature for CKEditor 5.", | ||
@@ -12,11 +12,11 @@ "keywords": [ | ||
"dependencies": { | ||
"@ckeditor/ckeditor5-core": "^10.1.0", | ||
"@ckeditor/ckeditor5-engine": "^10.1.0", | ||
"@ckeditor/ckeditor5-ui": "^10.1.0", | ||
"@ckeditor/ckeditor5-widget": "^10.1.0" | ||
"@ckeditor/ckeditor5-core": "^11.0.0", | ||
"@ckeditor/ckeditor5-engine": "^10.2.0", | ||
"@ckeditor/ckeditor5-ui": "^11.0.0", | ||
"@ckeditor/ckeditor5-widget": "^10.2.0" | ||
}, | ||
"devDependencies": { | ||
"@ckeditor/ckeditor5-editor-classic": "^10.0.1", | ||
"@ckeditor/ckeditor5-paragraph": "^10.0.1", | ||
"@ckeditor/ckeditor5-utils": "^10.1.0", | ||
"@ckeditor/ckeditor5-editor-classic": "^11.0.0", | ||
"@ckeditor/ckeditor5-paragraph": "^10.0.2", | ||
"@ckeditor/ckeditor5-utils": "^10.2.0", | ||
"eslint": "^4.15.0", | ||
@@ -28,3 +28,3 @@ "eslint-config-ckeditor5": "^1.0.7", | ||
"engines": { | ||
"node": ">=6.0.0", | ||
"node": ">=6.9.0", | ||
"npm": ">=3.0.0" | ||
@@ -31,0 +31,0 @@ }, |
@@ -14,2 +14,4 @@ /** | ||
import TableWalker from '../tablewalker'; | ||
import { updateNumericAttribute } from './utils'; | ||
import TableUtils from '../tableutils'; | ||
@@ -96,2 +98,6 @@ /** | ||
// Cache the parent of cell to remove for later check. | ||
const removedTableCellRow = cellToRemove.parent; | ||
// Remove table cell and merge it contents with merged cell. | ||
writer.move( Range.createIn( cellToRemove ), Position.createAt( cellToExpand, 'end' ) ); | ||
@@ -104,5 +110,10 @@ writer.remove( cellToRemove ); | ||
// Update table cell span attribute and merge set selection on merged contents. | ||
writer.setAttribute( spanAttribute, cellSpan + cellToMergeSpan, cellToExpand ); | ||
writer.setSelection( Range.createIn( cellToExpand ) ); | ||
writer.setSelection( Range.createIn( cellToExpand ) ); | ||
// Remove empty row after merging. | ||
if ( !removedTableCellRow.childCount ) { | ||
removeEmptyRow( removedTableCellRow, writer ); | ||
} | ||
} ); | ||
@@ -126,4 +137,8 @@ } | ||
const tableUtils = this.editor.plugins.get( TableUtils ); | ||
// First get the cell on proper direction. | ||
const cellToMerge = this.isHorizontal ? getHorizontalCell( element, this.direction ) : getVerticalCell( element, this.direction ); | ||
const cellToMerge = this.isHorizontal ? | ||
getHorizontalCell( element, this.direction, tableUtils ) : | ||
getVerticalCell( element, this.direction ); | ||
@@ -151,4 +166,24 @@ if ( !cellToMerge ) { | ||
// @returns {module:engine/model/node~Node|null} | ||
function getHorizontalCell( tableCell, direction ) { | ||
return direction == 'right' ? tableCell.nextSibling : tableCell.previousSibling; | ||
function getHorizontalCell( tableCell, direction, tableUtils ) { | ||
const horizontalCell = direction == 'right' ? tableCell.nextSibling : tableCell.previousSibling; | ||
if ( !horizontalCell ) { | ||
return; | ||
} | ||
// Sort cells: | ||
const cellOnLeft = direction == 'right' ? tableCell : horizontalCell; | ||
const cellOnRight = direction == 'right' ? horizontalCell : tableCell; | ||
// Get their column indexes: | ||
const { column: leftCellColumn } = tableUtils.getCellLocation( cellOnLeft ); | ||
const { column: rightCellColumn } = tableUtils.getCellLocation( cellOnRight ); | ||
const leftCellSpan = parseInt( cellOnLeft.getAttribute( 'colspan' ) || 1 ); | ||
// The cell on the right must have index that is distant to the cell on the left by the left cell's width (colspan). | ||
const cellsAreTouching = leftCellColumn + leftCellSpan === rightCellColumn; | ||
// If the right cell's column index is different it means that there are rowspanned cells between them. | ||
return cellsAreTouching ? horizontalCell : undefined; | ||
} | ||
@@ -203,1 +238,21 @@ | ||
} | ||
// Properly removes empty row from a table. Will update `rowspan` attribute of cells that overlaps removed row. | ||
// | ||
// @param {module:engine/model/element~Element} removedTableCellRow | ||
// @param {module:engine/model/writer~Writer} writer | ||
function removeEmptyRow( removedTableCellRow, writer ) { | ||
const table = removedTableCellRow.parent; | ||
const removedRowIndex = table.getChildIndex( removedTableCellRow ); | ||
for ( const { cell, row, rowspan } of new TableWalker( table, { endRow: removedRowIndex } ) ) { | ||
const overlapsRemovedRow = row + rowspan - 1 >= removedRowIndex; | ||
if ( overlapsRemovedRow ) { | ||
updateNumericAttribute( 'rowspan', rowspan - 1, cell, writer ); | ||
} | ||
} | ||
writer.remove( removedTableCellRow ); | ||
} |
@@ -98,3 +98,3 @@ /** | ||
const figureElement = conversionApi.mapper.toViewElement( table ); | ||
const tableElement = figureElement.getChild( 0 ); | ||
const tableElement = getViewTable( figureElement ); | ||
@@ -188,3 +188,3 @@ const row = table.getChildIndex( tableRow ); | ||
const figureElement = conversionApi.mapper.toViewElement( table ); | ||
const viewTable = figureElement.getChild( 0 ); | ||
const viewTable = getViewTable( figureElement ); | ||
@@ -502,1 +502,12 @@ const oldRows = data.attributeOldValue; | ||
} | ||
// Properly finds '<table>' element inside `<figure>` widget. | ||
// | ||
// @param {module:engine/view/element~Element} viewFigure | ||
function getViewTable( viewFigure ) { | ||
for ( const child of viewFigure.getChildren() ) { | ||
if ( child.name === 'table' ) { | ||
return child; | ||
} | ||
} | ||
} |
@@ -21,2 +21,7 @@ /** | ||
* | ||
* It loads the {@link module:table/tableediting~TableEditing table editing feature} | ||
* and {@link module:table/tableui~TableUI table UI feature}. | ||
* | ||
* For a detailed overview, check the {@glink features/table Table feature documentation}. | ||
* | ||
* @extends module:core/plugin~Plugin | ||
@@ -23,0 +28,0 @@ */ |
@@ -94,3 +94,3 @@ /** | ||
// Show balloon panel each time table widget is selected. | ||
this.listenTo( editor.editing.view, 'render', () => { | ||
this.listenTo( editor.ui, 'update', () => { | ||
this._checkIsVisible(); | ||
@@ -112,13 +112,8 @@ } ); | ||
const editor = this.editor; | ||
const viewSelection = editor.editing.view.document.selection; | ||
if ( !editor.ui.focusTracker.isFocused ) { | ||
if ( !editor.ui.focusTracker.isFocused || !isTableContentSelected( viewSelection ) ) { | ||
this._hideToolbar(); | ||
} else { | ||
const viewSelection = editor.editing.view.document.selection; | ||
if ( isTableContentSelected( viewSelection ) ) { | ||
this._showToolbar(); | ||
} else { | ||
this._hideToolbar(); | ||
} | ||
this._showToolbar(); | ||
} | ||
@@ -137,10 +132,8 @@ } | ||
repositionContextualBalloon( editor ); | ||
} else { | ||
if ( !this._balloon.hasView( this._toolbar ) ) { | ||
this._balloon.add( { | ||
view: this._toolbar, | ||
position: getBalloonPositionData( editor ), | ||
balloonClassName | ||
} ); | ||
} | ||
} else if ( !this._balloon.hasView( this._toolbar ) ) { | ||
this._balloon.add( { | ||
view: this._toolbar, | ||
position: getBalloonPositionData( editor ), | ||
balloonClassName | ||
} ); | ||
} | ||
@@ -147,0 +140,0 @@ } |
@@ -77,7 +77,32 @@ /** | ||
const options = [ | ||
{ commandName: 'setTableColumnHeader', label: t( 'Header column' ), bindIsActive: true }, | ||
'|', | ||
{ commandName: 'insertTableColumnBefore', label: t( 'Insert column before' ) }, | ||
{ commandName: 'insertTableColumnAfter', label: t( 'Insert column after' ) }, | ||
{ commandName: 'removeTableColumn', label: t( 'Delete column' ) } | ||
{ | ||
type: 'switchbutton', | ||
model: { | ||
commandName: 'setTableColumnHeader', | ||
label: t( 'Header column' ), | ||
bindIsOn: true | ||
} | ||
}, | ||
{ type: 'separator' }, | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'insertTableColumnBefore', | ||
label: t( 'Insert column before' ) | ||
} | ||
}, | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'insertTableColumnAfter', | ||
label: t( 'Insert column after' ) | ||
} | ||
}, | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'removeTableColumn', | ||
label: t( 'Delete column' ) | ||
} | ||
} | ||
]; | ||
@@ -90,7 +115,32 @@ | ||
const options = [ | ||
{ commandName: 'setTableRowHeader', label: t( 'Header row' ), bindIsActive: true }, | ||
'|', | ||
{ commandName: 'insertTableRowBelow', label: t( 'Insert row below' ) }, | ||
{ commandName: 'insertTableRowAbove', label: t( 'Insert row above' ) }, | ||
{ commandName: 'removeTableRow', label: t( 'Delete row' ) } | ||
{ | ||
type: 'switchbutton', | ||
model: { | ||
commandName: 'setTableRowHeader', | ||
label: t( 'Header row' ), | ||
bindIsOn: true | ||
} | ||
}, | ||
{ type: 'separator' }, | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'insertTableRowBelow', | ||
label: t( 'Insert row below' ) | ||
} | ||
}, | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'insertTableRowAbove', | ||
label: t( 'Insert row above' ) | ||
} | ||
}, | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'removeTableRow', | ||
label: t( 'Delete row' ) | ||
} | ||
} | ||
]; | ||
@@ -103,9 +153,45 @@ | ||
const options = [ | ||
{ commandName: 'mergeTableCellUp', label: t( 'Merge cell up' ) }, | ||
{ commandName: 'mergeTableCellRight', label: t( 'Merge cell right' ) }, | ||
{ commandName: 'mergeTableCellDown', label: t( 'Merge cell down' ) }, | ||
{ commandName: 'mergeTableCellLeft', label: t( 'Merge cell left' ) }, | ||
'|', | ||
{ commandName: 'splitTableCellVertically', label: t( 'Split cell vertically' ) }, | ||
{ commandName: 'splitTableCellHorizontally', label: t( 'Split cell horizontally' ) } | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'mergeTableCellUp', | ||
label: t( 'Merge cell up' ) | ||
} | ||
}, | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'mergeTableCellRight', | ||
label: t( 'Merge cell right' ) | ||
} | ||
}, | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'mergeTableCellDown', | ||
label: t( 'Merge cell down' ) | ||
} | ||
}, | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'mergeTableCellLeft', | ||
label: t( 'Merge cell left' ) | ||
} | ||
}, | ||
{ type: 'separator' }, | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'splitTableCellVertically', | ||
label: t( 'Split cell vertically' ) | ||
} | ||
}, | ||
{ | ||
type: 'button', | ||
model: { | ||
commandName: 'splitTableCellHorizontally', | ||
label: t( 'Split cell horizontally' ) | ||
} | ||
} | ||
]; | ||
@@ -123,3 +209,3 @@ | ||
* @param {String} icon An icon for the dropdown button. | ||
* @param {Array.<module:table/tableui~DropdownOption>} options The list of options for the dropdown. | ||
* @param {Array.<module:ui/dropdown/utils~ListDropdownItemDefinition>} options The list of options for the dropdown. | ||
* @param {module:utils/locale~Locale} locale | ||
@@ -135,9 +221,9 @@ * @returns {module:ui/dropdown/dropdownview~DropdownView} | ||
// Prepare dropdown list items for list dropdown. | ||
const dropdownItems = new Collection(); | ||
const itemDefinitions = new Collection(); | ||
for ( const option of options ) { | ||
addListOption( option, editor, commands, dropdownItems ); | ||
addListOption( option, editor, commands, itemDefinitions ); | ||
} | ||
addListToDropdown( dropdownView, dropdownItems ); | ||
addListToDropdown( dropdownView, itemDefinitions ); | ||
@@ -170,12 +256,9 @@ // Decorate dropdown's button. | ||
// @param {Array.<module:core/command~Command>} commands List of commands to update. | ||
// @param {module:utils/collection~Collection} dropdownItems Collection of dropdown items to update with given option. | ||
function addListOption( option, editor, commands, dropdownItems ) { | ||
const itemModel = new Model(); | ||
// @param {Iterable.<module:ui/dropdown/utils~ListDropdownItemDefinition>} itemDefinitions | ||
// Collection of dropdown items to update with given option. | ||
function addListOption( option, editor, commands, itemDefinitions ) { | ||
const model = option.model = new Model( option.model ); | ||
const { commandName, bindIsOn } = option.model; | ||
if ( option === '|' ) { | ||
itemModel.set( { | ||
isSeparator: true | ||
} ); | ||
} else { | ||
const { commandName, label, bindIsActive } = option; | ||
if ( option.type !== 'separator' ) { | ||
const command = editor.commands.get( commandName ); | ||
@@ -185,25 +268,16 @@ | ||
itemModel.set( { | ||
commandName, | ||
label | ||
} ); | ||
model.set( { commandName } ); | ||
itemModel.bind( 'isEnabled' ).to( command ); | ||
model.bind( 'isEnabled' ).to( command ); | ||
if ( bindIsActive ) { | ||
itemModel.bind( 'isActive' ).to( command, 'value' ); | ||
if ( bindIsOn ) { | ||
model.bind( 'isOn' ).to( command, 'value' ); | ||
} | ||
} | ||
dropdownItems.add( itemModel ); | ||
model.set( { | ||
withText: true | ||
} ); | ||
itemDefinitions.add( option ); | ||
} | ||
/** | ||
* An object describing the table dropdown items. | ||
* | ||
* @typedef {Object} module:table/tableui~DropdownOption | ||
* @private | ||
* @property {String} commandName A command name to execute for that option. | ||
* @property {String} label A dropdown item label. | ||
* @property {Boolean} bindIsActive If `true`, it will bind the command's value to the `isActive` dropdown item property. | ||
*/ |
@@ -28,3 +28,3 @@ /** | ||
return toWidget( viewElement, writer ); | ||
return toWidget( viewElement, writer, { hasSelectionHandler: true } ); | ||
} | ||
@@ -31,0 +31,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
168466
48
3333
- Removed@ckeditor/ckeditor5-core@10.1.0(transitive)
- Removed@ckeditor/ckeditor5-theme-lark@10.1.0(transitive)
- Removed@ckeditor/ckeditor5-ui@10.1.0(transitive)