Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table layout UI #17924

Merged
merged 11 commits into from
Feb 21, 2025
1 change: 1 addition & 0 deletions packages/ckeditor5-icons/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export { default as IconTableOfContents } from '../theme/icons/table-of-contents
export { default as IconTableProperties } from '../theme/icons/table-properties.svg';
export { default as IconTableRow } from '../theme/icons/table-row.svg';
export { default as IconTable } from '../theme/icons/table.svg';
export { default as IconTableLayout } from '../theme/icons/table.svg'; // TODO: Add table layout icon.
export { default as IconTemplateGeneric } from '../theme/icons/template-generic.svg';
export { default as IconTemplate } from '../theme/icons/template.svg';
export { default as IconTextAlternative } from '../theme/icons/text-alternative.svg';
Expand Down
4 changes: 3 additions & 1 deletion packages/ckeditor5-table/lang/contexts.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"Insert table": "Label for the insert table toolbar button.",
"Insert table layout": "Label for the insert table layout toolbar button.",
"Header column": "Label for the set/unset table header column button.",
"Insert column left": "Label for the insert table column to the left of the current one button.",
"Insert column right": "Label for the insert table column to the right of the current one button.",
Expand Down Expand Up @@ -62,5 +63,6 @@
"Move the selection to the previous cell": "Keystroke description for assistive technologies: keystroke for moving the selection to the previous cell.",
"Insert a new table row (when in the last cell of a table)": "Keystroke description for assistive technologies: keystroke for inserting a new table row.",
"Navigate through the table": "Keystroke description for assistive technologies: keystroke for navigating through the table.",
"Table": "The accessible label of the menu bar button that displays a user interface to insert a table into editor content."
"Table": "The accessible label of the menu bar button that displays a user interface to insert a table into editor content.",
"Table layout": "The accessible label of the menu bar button that displays a user interface to insert a table layout into editor content."
}
2 changes: 2 additions & 0 deletions packages/ckeditor5-table/src/augmentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import type {
InsertColumnCommand,
InsertRowCommand,
InsertTableCommand,
InsertTableLayoutCommand,
MergeCellCommand,
MergeCellsCommand,
RemoveColumnCommand,
Expand Down Expand Up @@ -107,6 +108,7 @@ declare module '@ckeditor/ckeditor5-core' {
insertTableRowAbove: InsertRowCommand;
insertTableRowBelow: InsertRowCommand;
insertTable: InsertTableCommand;
insertTableLayout: InsertTableLayoutCommand;
mergeTableCellRight: MergeCellCommand;
mergeTableCellLeft: MergeCellCommand;
mergeTableCellDown: MergeCellCommand;
Expand Down
33 changes: 33 additions & 0 deletions packages/ckeditor5-table/src/commands/inserttablelayoutcommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/

/**
* @module table/commands/inserttablecommand
*/

import InsertTableCommand from './inserttablecommand.js';
import type TableUtils from '../tableutils.js';

// TODO: This command will be implemented in the PR with editing part.
export default class InsertTableLayoutCommand extends InsertTableCommand {
public override execute(
options: {
rows?: number;
columns?: number;
}
): void {
const editor = this.editor;
const model = editor.model;
const tableUtils: TableUtils = editor.plugins.get( 'TableUtils' );

model.change( writer => {
const table = tableUtils.createTable( writer, options );

model.insertObject( table, null, null, { findOptimalPosition: 'auto' } );

writer.setSelection( writer.createPositionAt( table.getNodeByPath( [ 0, 0, 0 ] ), 0 ) );
} );
}
}
1 change: 1 addition & 0 deletions packages/ckeditor5-table/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type { TableConfig } from './tableconfig.js';
export type { default as InsertColumnCommand } from './commands/insertcolumncommand.js';
export type { default as InsertRowCommand } from './commands/insertrowcommand.js';
export type { default as InsertTableCommand } from './commands/inserttablecommand.js';
export type { default as InsertTableLayoutCommand } from './commands/inserttablelayoutcommand.js';
export type { default as MergeCellCommand } from './commands/mergecellcommand.js';
export type { default as MergeCellsCommand } from './commands/mergecellscommand.js';
export type { default as RemoveColumnCommand } from './commands/removecolumncommand.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default class ToggleTableCaptionCommand extends Command {
const editor = this.editor;
const tableElement = getSelectionAffectedTable( editor.model.document.selection );

this.isEnabled = !!tableElement;
this.isEnabled = !!tableElement && editor.model.schema.checkChild( tableElement, 'caption' );

if ( !this.isEnabled ) {
this.value = false;
Expand Down
8 changes: 8 additions & 0 deletions packages/ckeditor5-table/src/tablelayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@
*/

import { Plugin } from 'ckeditor5/src/core.js';
import TableLayoutUI from './tablelayout/tablelayoutui.js';

/**
* The table layout plugin.
*/
export default class TableLayout extends Plugin {
/**
* @inheritDoc
*/
public static get requires() {
return [ TableLayoutUI ] as const;
}

/**
* @inheritDoc
*/
Expand Down
123 changes: 123 additions & 0 deletions packages/ckeditor5-table/src/tablelayout/tablelayoutui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/

/**
* @module table/tablelayout/tablelayoutui
*/

import { Plugin } from 'ckeditor5/src/core.js';
import { IconTableLayout } from 'ckeditor5/src/icons.js'; // TODO: Remember to update in ckeditor5-icons when icon is ready.
import {
createDropdown,
MenuBarMenuView
} from 'ckeditor5/src/ui.js';
import type { ObservableChangeEvent } from 'ckeditor5/src/utils.js';

import InsertTableView from '../ui/inserttableview.js';
import InsertTableLayoutCommand from '../commands/inserttablelayoutcommand.js';

/**
* The table layout UI plugin. It introduces:
*
* * The `'insertTableLayout'` dropdown,
* * The `'menuBar:insertTableLayout'` menu bar menu.
*/
export default class TableLayoutUI extends Plugin {
/**
* @inheritDoc
*/
public static get pluginName() {
return 'TableLayoutUI' as const;
}

/**
* @inheritDoc
*/
public static override get isOfficialPlugin(): true {
return true;
}

/**
* @inheritDoc
*/
public init(): void {
const editor = this.editor;
const t = this.editor.t;

// TODO: Remove after merging with the editing part.
editor.commands.add( 'insertTableLayout', new InsertTableLayoutCommand( editor ) );

editor.ui.componentFactory.add( 'insertTableLayout', locale => {
const command: InsertTableLayoutCommand = editor.commands.get( 'insertTableLayout' )!;
const dropdownView = createDropdown( locale );

dropdownView.bind( 'isEnabled' ).to( command );

// Decorate dropdown's button.
dropdownView.buttonView.set( {
icon: IconTableLayout,
label: t( 'Insert table layout' ),
tooltip: true
} );

let insertTableLayoutView: InsertTableView;

dropdownView.on( 'change:isOpen', () => {
if ( insertTableLayoutView ) {
return;
}

// Prepare custom view for dropdown's panel.
insertTableLayoutView = new InsertTableView( locale );
dropdownView.panelView.children.add( insertTableLayoutView );

insertTableLayoutView.delegate( 'execute' ).to( dropdownView );

dropdownView.on( 'execute', () => {
editor.execute( 'insertTableLayout', {
rows: insertTableLayoutView.rows,
columns: insertTableLayoutView.columns
} );
editor.editing.view.focus();
} );
} );

return dropdownView;
} );

editor.ui.componentFactory.add( 'menuBar:insertTableLayout', locale => {
const command: InsertTableLayoutCommand = editor.commands.get( 'insertTableLayout' )!;
const menuView = new MenuBarMenuView( locale );
const insertTableLayoutView = new InsertTableView( locale );

insertTableLayoutView.delegate( 'execute' ).to( menuView );

menuView.on<ObservableChangeEvent<boolean>>( 'change:isOpen', ( event, name, isOpen ) => {
if ( !isOpen ) {
insertTableLayoutView.reset();
}
} );

insertTableLayoutView.on( 'execute', () => {
editor.execute( 'insertTableLayout', {
rows: insertTableLayoutView.rows,
columns: insertTableLayoutView.columns
} );
editor.editing.view.focus();
} );

menuView.buttonView.set( {
label: t( 'Table layout' ),
icon: IconTableLayout
} );

menuView.panelView.children.add( insertTableLayoutView );

menuView.bind( 'isEnabled' ).to( command );

return menuView;
} );
}
}
1 change: 1 addition & 0 deletions packages/ckeditor5-table/tests/manual/tablelayout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div id="editor"></div>
49 changes: 49 additions & 0 deletions packages/ckeditor5-table/tests/manual/tablelayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/

/* globals console, window, document */

import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor.js';
import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset.js';
import TableToolbar from '../../src/tabletoolbar.js';
import TableSelection from '../../src/tableselection.js';
import TableClipboard from '../../src/tableclipboard.js';
import TableProperties from '../../src/tableproperties.js';
import TableCellProperties from '../../src/tablecellproperties.js';
import TableCaption from '../../src/tablecaption.js';
import TableLayout from '../../src/tablelayout.js';

ClassicEditor
.create( document.querySelector( '#editor' ), {
image: { toolbar: [ 'toggleImageCaption', 'imageTextAlternative' ] },
plugins: [
ArticlePluginSet,
TableToolbar,
TableSelection,
TableClipboard,
TableProperties,
TableCellProperties,
TableCaption,
TableLayout
],
toolbar: [
'heading', '|', 'insertTable', 'insertTableLayout', '|', 'bold', 'italic', 'bulletedList', 'numberedList', 'blockQuote'
],
table: {
contentToolbar: [
'tableColumn', 'tableRow', 'mergeTableCells', 'tableProperties', 'tableCellProperties', 'toggleTableCaption'
],
tableToolbar: [ 'bold', 'italic' ]
},
menuBar: {
isVisible: true
}
} )
.then( editor => {
window.editor = editor;
} )
.catch( err => {
console.error( err.stack );
} );
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ describe( 'ToggleTableCaptionCommand', () => {
);
expect( command.isEnabled ).to.be.true;
} );

it( 'should be false if in the table that does not allow captions', () => {
editor.model.schema.extend( 'table', { disallowChildren: 'caption' } );

setData( model, modelTable( [ [ '[]' ] ] ) );

expect( command.isEnabled ).to.be.false;
} );
} );

describe( 'execute()', () => {
Expand Down
Loading