@ckeditor/ckeditor5-table
Version:
Table feature for CKEditor 5.
110 lines (109 loc) • 4.42 kB
JavaScript
/**
* @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/utils/ui/contextualballoon
*/
import { Rect } from 'ckeditor5/src/utils.js';
import { BalloonPanelView } from 'ckeditor5/src/ui.js';
import { getSelectionAffectedTableWidget, getTableWidgetAncestor } from './widget.js';
import { getSelectionAffectedTable } from '../common.js';
const BALLOON_POSITIONS = /* #__PURE__ */ (() => [
BalloonPanelView.defaultPositions.northArrowSouth,
BalloonPanelView.defaultPositions.northArrowSouthWest,
BalloonPanelView.defaultPositions.northArrowSouthEast,
BalloonPanelView.defaultPositions.southArrowNorth,
BalloonPanelView.defaultPositions.southArrowNorthWest,
BalloonPanelView.defaultPositions.southArrowNorthEast,
BalloonPanelView.defaultPositions.viewportStickyNorth
])();
/**
* A helper utility that positions the
* {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon} instance
* with respect to the table in the editor content, if one is selected.
*
* @param editor The editor instance.
* @param target Either "cell" or "table". Determines the target the balloon will be attached to.
*/
export function repositionContextualBalloon(editor, target) {
const balloon = editor.plugins.get('ContextualBalloon');
const selection = editor.editing.view.document.selection;
let position;
if (target === 'cell') {
if (getTableWidgetAncestor(selection)) {
position = getBalloonCellPositionData(editor);
}
}
else if (getSelectionAffectedTableWidget(selection)) {
position = getBalloonTablePositionData(editor);
}
if (position) {
balloon.updatePosition(position);
}
}
/**
* Returns the positioning options that control the geometry of the
* {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon} with respect
* to the selected table in the editor content.
*
* @param editor The editor instance.
*/
export function getBalloonTablePositionData(editor) {
const selection = editor.model.document.selection;
const modelTable = getSelectionAffectedTable(selection);
const viewTable = editor.editing.mapper.toViewElement(modelTable);
return {
target: editor.editing.view.domConverter.mapViewToDom(viewTable),
positions: BALLOON_POSITIONS
};
}
/**
* Returns the positioning options that control the geometry of the
* {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon} with respect
* to the selected table cell in the editor content.
*
* @param editor The editor instance.
*/
export function getBalloonCellPositionData(editor) {
const mapper = editor.editing.mapper;
const domConverter = editor.editing.view.domConverter;
const selection = editor.model.document.selection;
if (selection.rangeCount > 1) {
return {
target: () => createBoundingRect(selection.getRanges(), editor),
positions: BALLOON_POSITIONS
};
}
const modelTableCell = getTableCellAtPosition(selection.getFirstPosition());
const viewTableCell = mapper.toViewElement(modelTableCell);
return {
target: domConverter.mapViewToDom(viewTableCell),
positions: BALLOON_POSITIONS
};
}
/**
* Returns the first selected table cell from a multi-cell or in-cell selection.
*
* @param position Document position.
*/
function getTableCellAtPosition(position) {
const isTableCellSelected = position.nodeAfter && position.nodeAfter.is('element', 'tableCell');
return isTableCellSelected ? position.nodeAfter : position.findAncestor('tableCell');
}
/**
* Returns bounding rectangle for given model ranges.
*
* @param ranges Model ranges that the bounding rect should be returned for.
* @param editor The editor instance.
*/
function createBoundingRect(ranges, editor) {
const mapper = editor.editing.mapper;
const domConverter = editor.editing.view.domConverter;
const rects = Array.from(ranges).map(range => {
const modelTableCell = getTableCellAtPosition(range.start);
const viewTableCell = mapper.toViewElement(modelTableCell);
return new Rect(domConverter.mapViewToDom(viewTableCell));
});
return Rect.getBoundingRect(rects);
}