@ckeditor/ckeditor5-table
Version:
Table feature for CKEditor 5.
170 lines (169 loc) • 5.78 kB
JavaScript
/**
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module table/ui/inserttableview
*/
import { View, ButtonView, addKeyboardHandlingForGrid } from 'ckeditor5/src/ui';
import { KeystrokeHandler, FocusTracker } from 'ckeditor5/src/utils';
import './../../theme/inserttable.css';
/**
* The table size view.
*
* It renders a 10x10 grid to choose the inserted table size.
*/
export default class InsertTableView extends View {
/**
* @inheritDoc
*/
constructor(locale) {
super(locale);
const bind = this.bindTemplate;
this.items = this._createGridCollection();
this.keystrokes = new KeystrokeHandler();
this.focusTracker = new FocusTracker();
this.set('rows', 0);
this.set('columns', 0);
this.bind('label').to(this, 'columns', this, 'rows', (columns, rows) => `${rows} × ${columns}`);
this.setTemplate({
tag: 'div',
attributes: {
class: ['ck']
},
children: [
{
tag: 'div',
attributes: {
class: ['ck-insert-table-dropdown__grid']
},
on: {
'mouseover@.ck-insert-table-dropdown-grid-box': bind.to('boxover')
},
children: this.items
},
{
tag: 'div',
attributes: {
class: [
'ck',
'ck-insert-table-dropdown__label'
],
'aria-hidden': true
},
children: [
{
text: bind.to('label')
}
]
}
],
on: {
mousedown: bind.to(evt => {
evt.preventDefault();
}),
click: bind.to(() => {
this.fire('execute');
})
}
});
// #rows and #columns are set via changes to #focusTracker on mouse over.
this.on('boxover', (evt, domEvt) => {
const { row, column } = domEvt.target.dataset;
this.items.get((parseInt(row, 10) - 1) * 10 + (parseInt(column, 10) - 1)).focus();
});
// This allows the #rows and #columns to be updated when:
// * the user navigates the grid using the keyboard,
// * the user moves the mouse over grid items.
this.focusTracker.on('change:focusedElement', (evt, name, focusedElement) => {
if (!focusedElement) {
return;
}
const { row, column } = focusedElement.dataset;
// As row & column indexes are zero-based transform it to number of selected rows & columns.
this.set({
rows: parseInt(row),
columns: parseInt(column)
});
});
this.on('change:columns', () => this._highlightGridBoxes());
this.on('change:rows', () => this._highlightGridBoxes());
}
render() {
super.render();
addKeyboardHandlingForGrid({
keystrokeHandler: this.keystrokes,
focusTracker: this.focusTracker,
gridItems: this.items,
numberOfColumns: 10,
uiLanguageDirection: this.locale && this.locale.uiLanguageDirection
});
for (const item of this.items) {
this.focusTracker.add(item.element);
}
this.keystrokes.listenTo(this.element);
}
/**
* @inheritDoc
*/
focus() {
this.items.get(0).focus();
}
/**
* @inheritDoc
*/
focusLast() {
this.items.get(0).focus();
}
/**
* Highlights grid boxes depending on rows and columns selected.
*/
_highlightGridBoxes() {
const rows = this.rows;
const columns = this.columns;
this.items.map((boxView, index) => {
// Translate box index to the row & column index.
const itemRow = Math.floor(index / 10);
const itemColumn = index % 10;
// Grid box is highlighted when its row & column index belongs to selected number of rows & columns.
const isOn = itemRow < rows && itemColumn < columns;
boxView.set('isOn', isOn);
});
}
/**
* Creates a new Button for the grid.
*
* @param locale The locale instance.
* @param row Row number.
* @param column Column number.
* @param label The grid button label.
*/
_createGridButton(locale, row, column, label) {
const button = new ButtonView(locale);
button.set({
label,
class: 'ck-insert-table-dropdown-grid-box'
});
button.extendTemplate({
attributes: {
'data-row': row,
'data-column': column
}
});
return button;
}
/**
* @returns A view collection containing boxes to be placed in a table grid.
*/
_createGridCollection() {
const boxes = [];
// Add grid boxes to table selection view.
for (let index = 0; index < 100; index++) {
const row = Math.floor(index / 10);
const column = index % 10;
const label = `${row + 1} × ${column + 1}`;
boxes.push(this._createGridButton(this.locale, row + 1, column + 1, label));
}
return this.createCollection(boxes);
}
}