-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
Copy pathcontextualballoon.js
144 lines (122 loc) · 4.98 KB
/
contextualballoon.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/**
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module table/utils/ui/contextualballoon
*/
import { centeredBalloonPositionForLongWidgets } from '@ckeditor/ckeditor5-widget/src/utils';
import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
import { getTableWidgetAncestor } from './widget';
import BalloonPanelView from '@ckeditor/ckeditor5-ui/src/panel/balloon/balloonpanelview';
const DEFAULT_BALLOON_POSITIONS = BalloonPanelView.defaultPositions;
const BALLOON_POSITIONS = [
DEFAULT_BALLOON_POSITIONS.northArrowSouth,
DEFAULT_BALLOON_POSITIONS.northArrowSouthWest,
DEFAULT_BALLOON_POSITIONS.northArrowSouthEast,
DEFAULT_BALLOON_POSITIONS.southArrowNorth,
DEFAULT_BALLOON_POSITIONS.southArrowNorthWest,
DEFAULT_BALLOON_POSITIONS.southArrowNorthEast
];
const TABLE_PROPERTIES_BALLOON_POSITIONS = [
...BALLOON_POSITIONS,
centeredBalloonPositionForLongWidgets
];
/**
* 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 {module:core/editor/editor~Editor} editor The editor instance.
* @param {String} 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' );
if ( getTableWidgetAncestor( editor.editing.view.document.selection ) ) {
let position;
if ( target === 'cell' ) {
position = getBalloonCellPositionData( editor );
} else {
position = getBalloonTablePositionData( editor );
}
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 {module:core/editor/editor~Editor} editor The editor instance.
* @returns {module:utils/dom/position~Options}
*/
export function getBalloonTablePositionData( editor ) {
const firstPosition = editor.model.document.selection.getFirstPosition();
const modelTable = firstPosition.findAncestor( 'table' );
const viewTable = editor.editing.mapper.toViewElement( modelTable );
return {
target: editor.editing.view.domConverter.viewToDom( viewTable ),
positions: TABLE_PROPERTIES_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 {module:core/editor/editor~Editor} editor The editor instance.
* @returns {module:utils/dom/position~Options}
*/
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(), modelRange => {
const modelTableCell = getTableCellAtPosition( modelRange.start );
const viewTableCell = mapper.toViewElement( modelTableCell );
return new Rect( domConverter.viewToDom( viewTableCell ) );
} ),
positions: BALLOON_POSITIONS
};
}
const modelTableCell = getTableCellAtPosition( selection.getFirstPosition() );
const viewTableCell = mapper.toViewElement( modelTableCell );
return {
target: domConverter.viewToDom( viewTableCell ),
positions: BALLOON_POSITIONS
};
}
// Returns the first selected table cell from a multi-cell or in-cell selection.
//
// @param {module:engine/model/position~Position} position Document position.
// @returns {module:engine/model/element~Element}
function getTableCellAtPosition( position ) {
const isTableCellSelected = position.nodeAfter && position.nodeAfter.is( 'element', 'tableCell' );
return isTableCellSelected ? position.nodeAfter : position.findAncestor( 'tableCell' );
}
// Returns bounding rect for list of rects.
//
// @param {Array.<module:utils/dom/rect~Rect>|Array.<*>} list List of `Rect`s or any list to map by `mapFn`.
// @param {Function} mapFn Mapping function for list elements.
// @returns {module:utils/dom/rect~Rect}
function createBoundingRect( list, mapFn ) {
const rectData = {
left: Number.POSITIVE_INFINITY,
top: Number.POSITIVE_INFINITY,
right: Number.NEGATIVE_INFINITY,
bottom: Number.NEGATIVE_INFINITY
};
for ( const item of list ) {
const rect = mapFn( item );
rectData.left = Math.min( rectData.left, rect.left );
rectData.top = Math.min( rectData.top, rect.top );
rectData.right = Math.max( rectData.right, rect.right );
rectData.bottom = Math.max( rectData.bottom, rect.bottom );
}
rectData.width = rectData.right - rectData.left;
rectData.height = rectData.bottom - rectData.top;
return new Rect( rectData );
}