Socket
Socket
Sign inDemoInstall

@ckeditor/ckeditor5-table

Package Overview
Dependencies
Maintainers
1
Versions
634
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ckeditor/ckeditor5-table - npm Package Compare versions

Comparing version 23.0.0 to 23.1.0

lang/translations/hi.po

36

package.json
{
"name": "@ckeditor/ckeditor5-table",
"version": "23.0.0",
"version": "23.1.0",
"description": "Table feature for CKEditor 5.",

@@ -13,22 +13,22 @@ "keywords": [

"dependencies": {
"@ckeditor/ckeditor5-core": "^23.0.0",
"@ckeditor/ckeditor5-ui": "^23.0.0",
"@ckeditor/ckeditor5-widget": "^23.0.0",
"@ckeditor/ckeditor5-core": "^23.1.0",
"@ckeditor/ckeditor5-ui": "^23.1.0",
"@ckeditor/ckeditor5-widget": "^23.1.0",
"lodash-es": "^4.17.15"
},
"devDependencies": {
"@ckeditor/ckeditor5-alignment": "^23.0.0",
"@ckeditor/ckeditor5-block-quote": "^23.0.0",
"@ckeditor/ckeditor5-clipboard": "^23.0.0",
"@ckeditor/ckeditor5-editor-classic": "^23.0.0",
"@ckeditor/ckeditor5-engine": "^23.0.0",
"@ckeditor/ckeditor5-horizontal-line": "^23.0.0",
"@ckeditor/ckeditor5-image": "^23.0.0",
"@ckeditor/ckeditor5-indent": "^23.0.0",
"@ckeditor/ckeditor5-list": "^23.0.0",
"@ckeditor/ckeditor5-media-embed": "^23.0.0",
"@ckeditor/ckeditor5-paragraph": "^23.0.0",
"@ckeditor/ckeditor5-typing": "^23.0.0",
"@ckeditor/ckeditor5-undo": "^23.0.0",
"@ckeditor/ckeditor5-utils": "^23.0.0",
"@ckeditor/ckeditor5-alignment": "^23.1.0",
"@ckeditor/ckeditor5-block-quote": "^23.1.0",
"@ckeditor/ckeditor5-clipboard": "^23.1.0",
"@ckeditor/ckeditor5-editor-classic": "^23.1.0",
"@ckeditor/ckeditor5-engine": "^23.1.0",
"@ckeditor/ckeditor5-horizontal-line": "^23.1.0",
"@ckeditor/ckeditor5-image": "^23.1.0",
"@ckeditor/ckeditor5-indent": "^23.1.0",
"@ckeditor/ckeditor5-list": "^23.1.0",
"@ckeditor/ckeditor5-media-embed": "^23.1.0",
"@ckeditor/ckeditor5-paragraph": "^23.1.0",
"@ckeditor/ckeditor5-typing": "^23.1.0",
"@ckeditor/ckeditor5-undo": "^23.1.0",
"@ckeditor/ckeditor5-utils": "^23.1.0",
"json-diff": "^0.5.4"

@@ -35,0 +35,0 @@ },

@@ -11,3 +11,3 @@ /**

import TableWalker from './../tablewalker';
import { toWidget, toWidgetEditable, setHighlightHandling } from '@ckeditor/ckeditor5-widget/src/utils';
import { setHighlightHandling, toWidget, toWidgetEditable } from '@ckeditor/ckeditor5-widget/src/utils';

@@ -239,2 +239,50 @@ /**

/**
* Overrides paragraph inside table cell conversion.
*
* This converter:
* * should be used to override default paragraph conversion in the editing view.
* * It will only convert <paragraph> placed directly inside <tableCell>.
* * For a single paragraph without attributes it returns `<span>` to simulate data table.
* * For all other cases it returns `<p>` element.
*
* @param {module:engine/model/element~Element} modelElement
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
* @returns {module:engine/view/containerelement~ContainerElement|undefined}
*/
export function convertParagraphInTableCell( modelElement, conversionApi ) {
const { writer } = conversionApi;
if ( !modelElement.parent.is( 'element', 'tableCell' ) ) {
return;
}
if ( isSingleParagraphWithoutAttributes( modelElement ) ) {
// Use display:inline-block to force Chrome/Safari to limit text mutations to this element.
// See #6062.
return writer.createContainerElement( 'span', { style: 'display:inline-block' } );
} else {
return writer.createContainerElement( 'p' );
}
}
/**
* Checks if given model `<paragraph>` is an only child of a parent (`<tableCell>`) and if it has any attribute set.
*
* The paragraph should be converted in the editing view to:
*
* * If returned `true` - to a `<span style="display:inline-block">`
* * If returned `false` - to a `<p>`
*
* @param {module:engine/model/element~Element} modelElement
* @returns {Boolean}
*/
export function isSingleParagraphWithoutAttributes( modelElement ) {
const tableCell = modelElement.parent;
const isSingleParagraph = tableCell.childCount === 1;
return isSingleParagraph && !hasAnyAttribute( modelElement );
}
// Converts a given {@link module:engine/view/element~Element} to a table widget:

@@ -332,23 +380,11 @@ // * Adds a {@link module:engine/view/element~Element#_setCustomProperty custom property} allowing to recognize the table widget element.

if ( isSingleParagraph && !hasAnyAttribute( firstChild ) ) {
conversionApi.mapper.bindElements( tableCell, cellElement );
// Additional requirement for data pipeline to have backward compatible data tables.
if ( !asWidget && !hasAnyAttribute( firstChild ) && isSingleParagraph ) {
const innerParagraph = tableCell.getChild( 0 );
const paragraphInsertPosition = conversionApi.writer.createPositionAt( cellElement, 'end' );
conversionApi.consumable.consume( innerParagraph, 'insert' );
if ( asWidget ) {
// Use display:inline-block to force Chrome/Safari to limit text mutations to this element.
// See #6062.
const fakeParagraph = conversionApi.writer.createContainerElement( 'span', { style: 'display:inline-block' } );
conversionApi.mapper.bindElements( innerParagraph, fakeParagraph );
conversionApi.writer.insert( paragraphInsertPosition, fakeParagraph );
conversionApi.mapper.bindElements( tableCell, cellElement );
} else {
conversionApi.mapper.bindElements( tableCell, cellElement );
conversionApi.mapper.bindElements( innerParagraph, cellElement );
}
} else {
conversionApi.mapper.bindElements( tableCell, cellElement );
conversionApi.mapper.bindElements( innerParagraph, cellElement );
}

@@ -355,0 +391,0 @@ }

@@ -10,2 +10,4 @@ /**

import { isSingleParagraphWithoutAttributes } from './downcast';
/**

@@ -21,85 +23,55 @@ * Injects a table cell post-fixer into the model which marks the table cell in the differ to have it re-rendered.

* @param {module:engine/model/model~Model} model
* @param {module:engine/conversion/mapper~Mapper} mapper
*/
export default function injectTableCellRefreshPostFixer( model ) {
model.document.registerPostFixer( () => tableCellRefreshPostFixer( model ) );
export default function injectTableCellRefreshPostFixer( model, mapper ) {
model.document.registerPostFixer( () => tableCellRefreshPostFixer( model.document.differ, mapper ) );
}
function tableCellRefreshPostFixer( model ) {
const differ = model.document.differ;
function tableCellRefreshPostFixer( differ, mapper ) {
// Stores cells to be refreshed, so the table cell will be refreshed once for multiple changes.
// Stores cells to be refreshed so the table cell will be refreshed once for multiple changes.
const cellsToRefresh = new Set();
// 1. Gather all changes inside table cell.
const cellsToCheck = new Set();
// Counting the paragraph inserts to verify if it increased to more than one paragraph in the current differ.
let insertCount = 0;
for ( const change of differ.getChanges() ) {
const parent = change.type == 'insert' || change.type == 'remove' ? change.position.parent : change.range.start.parent;
const parent = change.type == 'attribute' ? change.range.start.parent : change.position.parent;
if ( !parent.is( 'element', 'tableCell' ) ) {
continue;
if ( parent.is( 'element', 'tableCell' ) ) {
cellsToCheck.add( parent );
}
if ( change.type == 'insert' ) {
insertCount++;
}
if ( checkRefresh( parent, change.type, insertCount ) ) {
cellsToRefresh.add( parent );
}
}
if ( cellsToRefresh.size ) {
// @if CK_DEBUG_TABLE // console.log( `Post-fixing table: refreshing cells (${ cellsToRefresh.size }).` );
// @if CK_DEBUG_TABLE // console.log( `Post-fixing table: Checking table cell to refresh (${ cellsToCheck.size }).` );
// @if CK_DEBUG_TABLE // let paragraphsRefreshed = 0;
for ( const tableCell of cellsToRefresh.values() ) {
differ.refreshItem( tableCell );
for ( const tableCell of cellsToCheck.values() ) {
for ( const paragraph of [ ...tableCell.getChildren() ].filter( child => shouldRefresh( child, mapper ) ) ) {
// @if CK_DEBUG_TABLE // console.log( `Post-fixing table: refreshing paragraph in table cell (${++paragraphsRefreshed}).` );
differ.refreshItem( paragraph );
}
return true;
}
// Always return false to prevent the refresh post-fixer from re-running on the same set of changes and going into an infinite loop.
// This "post-fixer" does not change the model structure so there shouldn't be need to run other post-fixers again.
// See https://github.com/ckeditor/ckeditor5/issues/1936 & https://github.com/ckeditor/ckeditor5/issues/8200.
return false;
}
// Checks if the model table cell requires refreshing to be re-rendered to a proper state in the view.
// Check if given model element needs refreshing.
//
// This method detects changes that will require renaming `<span>` to `<p>` (or vice versa) in the view.
//
// This method is a simple heuristic that checks only a single change and will sometimes give a false positive result when multiple changes
// will result in a state that does not require renaming in the view (but will be seen as requiring a refresh).
//
// For instance: A `<span>` should be renamed to `<p>` when adding an attribute to a `<paragraph>`.
// But adding one attribute and removing another one will result in a false positive: the check for an added attribute will see one
// attribute on a paragraph and will falsely qualify such change as adding an attribute to a paragraph without any attribute.
//
// @param {module:engine/model/element~Element} tableCell The table cell to check.
// @param {String} type Type of change.
// @param {Number} insertCount The number of inserts in differ.
function checkRefresh( tableCell, type, insertCount ) {
const hasInnerParagraph = Array.from( tableCell.getChildren() ).some( child => child.is( 'element', 'paragraph' ) );
// If there is no paragraph in table cell then the view doesn't require refreshing.
//
// Why? What we really want to achieve is to make all the old paragraphs (which weren't added in this batch) to be
// converted once again, so that the paragraph-in-table-cell converter can correctly create a `<p>` or a `<span>` element.
// If there are no paragraphs in the table cell, we don't care.
if ( !hasInnerParagraph ) {
// @param {module:engine/model/element~Element} modelElement
// @param {module:engine/conversion/mapper~Mapper} mapper
// @returns {Boolean}
function shouldRefresh( child, mapper ) {
if ( !child.is( 'element', 'paragraph' ) ) {
return false;
}
// For attribute change we only refresh if there is a single paragraph as in this case we may want to change existing `<span>` to `<p>`.
if ( type == 'attribute' ) {
const attributesCount = Array.from( tableCell.getChild( 0 ).getAttributeKeys() ).length;
const viewElement = mapper.toViewElement( child );
return tableCell.childCount === 1 && attributesCount < 2;
if ( !viewElement ) {
return false;
}
// For other changes (insert, remove) the `<span>` to `<p>` change is needed when:
//
// - another element is added to a single paragraph (childCount becomes >= 2)
// - another element is removed and a single paragraph is left (childCount == 1)
//
// Change is not needed if there were multiple blocks before change.
return tableCell.childCount <= ( type == 'insert' ? insertCount + 1 : 1 );
return isSingleParagraphWithoutAttributes( child ) !== viewElement.is( 'element', 'span' );
}

@@ -47,2 +47,3 @@ /**

for ( const table of tablesToRefresh.values() ) {
// Should be handled by a `triggerBy` configuration. See: https://github.com/ckeditor/ckeditor5/issues/8138.
differ.refreshItem( table );

@@ -49,0 +50,0 @@ }

@@ -87,27 +87,2 @@ /**

/**
* A converter that ensures an empty paragraph is inserted in a table cell if no other content was converted.
*
* @returns {Function} Conversion helper.
*/
export function ensureParagraphInTableCell( elementName ) {
return dispatcher => {
dispatcher.on( `element:${ elementName }`, ( evt, data, conversionApi ) => {
// The default converter will create a model range on converted table cell.
if ( !data.modelRange ) {
return;
}
const tableCell = data.modelRange.start.nodeAfter;
// Ensure a paragraph in the model for empty table cells for converted table cells.
if ( !tableCell.childCount ) {
const modelCursor = conversionApi.writer.createPositionAt( tableCell, 0 );
conversionApi.writer.insertElement( 'paragraph', modelCursor );
}
}, { priority: 'low' } );
};
}
// Scans table rows and extracts required metadata from the table:

@@ -114,0 +89,0 @@ //

@@ -59,2 +59,4 @@ /**

this.listenTo( editor.model, 'insertContent', ( evt, args ) => this._onInsertContent( evt, ...args ), { priority: 'high' } );
this.decorate( '_replaceTableSlotCell' );
}

@@ -168,3 +170,3 @@

const cellsToSelect = replaceSelectedCellsWithPasted( pastedTable, pastedDimensions, selectedTable, selection, writer );
const cellsToSelect = this._replaceSelectedCellsWithPasted( pastedTable, pastedDimensions, selectedTable, selection, writer );

@@ -183,4 +185,187 @@ if ( this.editor.plugins.get( 'TableSelection' ).isEnabled ) {

}
/**
* Replaces the part of selectedTable with pastedTable.
*
* @private
* @param {module:engine/model/element~Element} pastedTable
* @param {Object} pastedDimensions
* @param {Number} pastedDimensions.height
* @param {Number} pastedDimensions.width
* @param {module:engine/model/element~Element} selectedTable
* @param {Object} selection
* @param {Number} selection.firstColumn
* @param {Number} selection.firstRow
* @param {Number} selection.lastColumn
* @param {Number} selection.lastRow
* @param {module:engine/model/writer~Writer} writer
* @returns {Array.<module:engine/model/element~Element>}
*/
_replaceSelectedCellsWithPasted( pastedTable, pastedDimensions, selectedTable, selection, writer ) {
const { width: pastedWidth, height: pastedHeight } = pastedDimensions;
// Holds two-dimensional array that is addressed by [ row ][ column ] that stores cells anchored at given location.
const pastedTableLocationMap = createLocationMap( pastedTable, pastedWidth, pastedHeight );
const selectedTableMap = [ ...new TableWalker( selectedTable, {
startRow: selection.firstRow,
endRow: selection.lastRow,
startColumn: selection.firstColumn,
endColumn: selection.lastColumn,
includeAllSlots: true
} ) ];
// Selection must be set to pasted cells (some might be removed or new created).
const cellsToSelect = [];
// Store next cell insert position.
let insertPosition;
// Content table replace cells algorithm iterates over a selected table fragment and:
//
// - Removes existing table cells at current slot (location).
// - Inserts cell from a pasted table for a matched slots.
//
// This ensures proper table geometry after the paste
for ( const tableSlot of selectedTableMap ) {
const { row, column } = tableSlot;
// Save the insert position for current row start.
if ( column === selection.firstColumn ) {
insertPosition = tableSlot.getPositionBefore();
}
// Map current table slot location to an pasted table slot location.
const pastedRow = row - selection.firstRow;
const pastedColumn = column - selection.firstColumn;
const pastedCell = pastedTableLocationMap[ pastedRow % pastedHeight ][ pastedColumn % pastedWidth ];
// Clone cell to insert (to duplicate its attributes and children).
// Cloning is required to support repeating pasted table content when inserting to a bigger selection.
const cellToInsert = pastedCell ? writer.cloneElement( pastedCell ) : null;
// Replace the cell from the current slot with new table cell.
const newTableCell = this._replaceTableSlotCell( tableSlot, cellToInsert, insertPosition, writer );
// The cell was only removed.
if ( !newTableCell ) {
continue;
}
// Trim the cell if it's row/col-spans would exceed selection area.
trimTableCellIfNeeded( newTableCell, row, column, selection.lastRow, selection.lastColumn, writer );
cellsToSelect.push( newTableCell );
insertPosition = writer.createPositionAfter( newTableCell );
}
// If there are any headings, all the cells that overlap from heading must be splitted.
const headingRows = parseInt( selectedTable.getAttribute( 'headingRows' ) || 0 );
const headingColumns = parseInt( selectedTable.getAttribute( 'headingColumns' ) || 0 );
const areHeadingRowsIntersectingSelection = selection.firstRow < headingRows && headingRows <= selection.lastRow;
const areHeadingColumnsIntersectingSelection = selection.firstColumn < headingColumns && headingColumns <= selection.lastColumn;
if ( areHeadingRowsIntersectingSelection ) {
const columnsLimit = { first: selection.firstColumn, last: selection.lastColumn };
const newCells = doHorizontalSplit( selectedTable, headingRows, columnsLimit, writer, selection.firstRow );
cellsToSelect.push( ...newCells );
}
if ( areHeadingColumnsIntersectingSelection ) {
const rowsLimit = { first: selection.firstRow, last: selection.lastRow };
const newCells = doVerticalSplit( selectedTable, headingColumns, rowsLimit, writer );
cellsToSelect.push( ...newCells );
}
return cellsToSelect;
}
/**
* Replaces a single table slot.
*
* @private
* @param {module:table/tablewalker~TableSlot} tableSlot
* @param {module:engine/model/element~Element} cellToInsert
* @param {module:engine/model/position~Position} insertPosition
* @param {module:engine/model/writer~Writer} writer
* @returns {module:engine/model/element~Element|null} Inserted table cell or null if slot should remain empty.
*/
_replaceTableSlotCell( tableSlot, cellToInsert, insertPosition, writer ) {
const { cell, isAnchor } = tableSlot;
// If the slot is occupied by a cell in a selected table - remove it.
// The slot of this cell will be either:
// - Replaced by a pasted table cell.
// - Spanned by a previously pasted table cell.
if ( isAnchor ) {
writer.remove( cell );
}
// There is no cell to insert (might be spanned by other cell in a pasted table) - advance to the next content table slot.
if ( !cellToInsert ) {
return null;
}
writer.insert( cellToInsert, insertPosition );
return cellToInsert;
}
}
/**
* Extract table for pasting into table.
*
* @private
* @param {module:engine/model/documentfragment~DocumentFragment|module:engine/model/item~Item} content The content to insert.
* @param {module:engine/model/model~Model} model The editor model.
* @returns {module:engine/model/element~Element|null}
*/
export function getTableIfOnlyTableInContent( content, model ) {
if ( !content.is( 'documentFragment' ) && !content.is( 'element' ) ) {
return null;
}
// Table passed directly.
if ( content.is( 'element', 'table' ) ) {
return content;
}
// We do not support mixed content when pasting table into table.
// See: https://github.com/ckeditor/ckeditor5/issues/6817.
if ( content.childCount == 1 && content.getChild( 0 ).is( 'element', 'table' ) ) {
return content.getChild( 0 );
}
// If there are only whitespaces around a table then use that table for pasting.
const contentRange = model.createRangeIn( content );
for ( const element of contentRange.getItems() ) {
if ( element.is( 'element', 'table' ) ) {
// Stop checking if there is some content before table.
const rangeBefore = model.createRange( contentRange.start, model.createPositionBefore( element ) );
if ( model.hasContent( rangeBefore, { ignoreWhitespaces: true } ) ) {
return null;
}
// Stop checking if there is some content after table.
const rangeAfter = model.createRange( model.createPositionAfter( element ), contentRange.end );
if ( model.hasContent( rangeAfter, { ignoreWhitespaces: true } ) ) {
return null;
}
// There wasn't any content neither before nor after.
return element;
}
}
return null;
}
// Prepares a table for pasting and returns adjusted selection dimensions.

@@ -252,105 +437,2 @@ //

// Replaces the part of selectedTable with pastedTable.
//
// @param {module:engine/model/element~Element} pastedTable
// @param {Object} pastedDimensions
// @param {Number} pastedDimensions.height
// @param {Number} pastedDimensions.width
// @param {module:engine/model/element~Element} selectedTable
// @param {Object} selection
// @param {Number} selection.firstColumn
// @param {Number} selection.firstRow
// @param {Number} selection.lastColumn
// @param {Number} selection.lastRow
// @param {module:engine/model/writer~Writer} writer
// @returns {Array.<module:engine/model/element~Element>}
function replaceSelectedCellsWithPasted( pastedTable, pastedDimensions, selectedTable, selection, writer ) {
const { width: pastedWidth, height: pastedHeight } = pastedDimensions;
// Holds two-dimensional array that is addressed by [ row ][ column ] that stores cells anchored at given location.
const pastedTableLocationMap = createLocationMap( pastedTable, pastedWidth, pastedHeight );
const selectedTableMap = [ ...new TableWalker( selectedTable, {
startRow: selection.firstRow,
endRow: selection.lastRow,
startColumn: selection.firstColumn,
endColumn: selection.lastColumn,
includeAllSlots: true
} ) ];
// Selection must be set to pasted cells (some might be removed or new created).
const cellsToSelect = [];
// Store next cell insert position.
let insertPosition;
// Content table replace cells algorithm iterates over a selected table fragment and:
//
// - Removes existing table cells at current slot (location).
// - Inserts cell from a pasted table for a matched slots.
//
// This ensures proper table geometry after the paste
for ( const tableSlot of selectedTableMap ) {
const { row, column, cell, isAnchor } = tableSlot;
// Save the insert position for current row start.
if ( column === selection.firstColumn ) {
insertPosition = tableSlot.getPositionBefore();
}
// If the slot is occupied by a cell in a selected table - remove it.
// The slot of this cell will be either:
// - Replaced by a pasted table cell.
// - Spanned by a previously pasted table cell.
if ( isAnchor ) {
writer.remove( cell );
}
// Map current table slot location to an pasted table slot location.
const pastedRow = row - selection.firstRow;
const pastedColumn = column - selection.firstColumn;
const pastedCell = pastedTableLocationMap[ pastedRow % pastedHeight ][ pastedColumn % pastedWidth ];
// There is no cell to insert (might be spanned by other cell in a pasted table) - advance to the next content table slot.
if ( !pastedCell ) {
continue;
}
// Clone cell to insert (to duplicate its attributes and children).
// Cloning is required to support repeating pasted table content when inserting to a bigger selection.
const cellToInsert = writer.cloneElement( pastedCell );
// Trim the cell if it's row/col-spans would exceed selection area.
trimTableCellIfNeeded( cellToInsert, row, column, selection.lastRow, selection.lastColumn, writer );
writer.insert( cellToInsert, insertPosition );
cellsToSelect.push( cellToInsert );
insertPosition = writer.createPositionAfter( cellToInsert );
}
// If there are any headings, all the cells that overlap from heading must be splitted.
const headingRows = parseInt( selectedTable.getAttribute( 'headingRows' ) || 0 );
const headingColumns = parseInt( selectedTable.getAttribute( 'headingColumns' ) || 0 );
const areHeadingRowsIntersectingSelection = selection.firstRow < headingRows && headingRows <= selection.lastRow;
const areHeadingColumnsIntersectingSelection = selection.firstColumn < headingColumns && headingColumns <= selection.lastColumn;
if ( areHeadingRowsIntersectingSelection ) {
const columnsLimit = { first: selection.firstColumn, last: selection.lastColumn };
const newCells = doHorizontalSplit( selectedTable, headingRows, columnsLimit, writer, selection.firstRow );
cellsToSelect.push( ...newCells );
}
if ( areHeadingColumnsIntersectingSelection ) {
const rowsLimit = { first: selection.firstRow, last: selection.lastRow };
const newCells = doVerticalSplit( selectedTable, headingColumns, rowsLimit, writer );
cellsToSelect.push( ...newCells );
}
return cellsToSelect;
}
// Expand table (in place) to expected size.

@@ -376,46 +458,2 @@ function expandTableSize( table, expectedHeight, expectedWidth, tableUtils ) {

function getTableIfOnlyTableInContent( content, model ) {
if ( !content.is( 'documentFragment' ) && !content.is( 'element' ) ) {
return null;
}
// Table passed directly.
if ( content.is( 'element', 'table' ) ) {
return content;
}
// We do not support mixed content when pasting table into table.
// See: https://github.com/ckeditor/ckeditor5/issues/6817.
if ( content.childCount == 1 && content.getChild( 0 ).is( 'element', 'table' ) ) {
return content.getChild( 0 );
}
// If there are only whitespaces around a table then use that table for pasting.
const contentRange = model.createRangeIn( content );
for ( const element of contentRange.getItems() ) {
if ( element.is( 'element', 'table' ) ) {
// Stop checking if there is some content before table.
const rangeBefore = model.createRange( contentRange.start, model.createPositionBefore( element ) );
if ( model.hasContent( rangeBefore, { ignoreWhitespaces: true } ) ) {
return null;
}
// Stop checking if there is some content after table.
const rangeAfter = model.createRange( model.createPositionAfter( element ), contentRange.end );
if ( model.hasContent( rangeAfter, { ignoreWhitespaces: true } ) ) {
return null;
}
// There wasn't any content neither before nor after.
return element;
}
}
return null;
}
// Returns two-dimensional array that is addressed by [ row ][ column ] that stores cells anchored at given location.

@@ -422,0 +460,0 @@ //

@@ -12,4 +12,5 @@ /**

import upcastTable, { ensureParagraphInTableCell, skipEmptyTableRow } from './converters/upcasttable';
import upcastTable, { skipEmptyTableRow } from './converters/upcasttable';
import {
convertParagraphInTableCell,
downcastInsertCell,

@@ -110,7 +111,12 @@ downcastInsertRow,

conversion.for( 'upcast' ).elementToElement( { model: 'tableCell', view: 'th' } );
conversion.for( 'upcast' ).add( ensureParagraphInTableCell( 'td' ) );
conversion.for( 'upcast' ).add( ensureParagraphInTableCell( 'th' ) );
conversion.for( 'editingDowncast' ).add( downcastInsertCell() );
// Duplicates code - needed to properly refresh paragraph inside table cell.
editor.conversion.for( 'editingDowncast' ).elementToElement( {
model: 'paragraph',
view: convertParagraphInTableCell,
converterPriority: 'high'
} );
// Table attributes conversion.

@@ -151,3 +157,3 @@ conversion.attributeToAttribute( { model: 'colspan', view: 'colspan' } );

injectTableLayoutPostFixer( model );
injectTableCellRefreshPostFixer( model );
injectTableCellRefreshPostFixer( model, editor.editing.mapper );
injectTableCellParagraphPostFixer( model );

@@ -154,0 +160,0 @@ }

@@ -30,2 +30,10 @@ /**

/**
* @inheritDoc
*/
init() {
this.decorate( 'insertColumns' );
this.decorate( 'insertRows' );
}
/**
* Returns the table cell location as an object with table row and table column indexes.

@@ -32,0 +40,0 @@ *

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc