@ckeditor/ckeditor5-ui 38.1.0 → 38.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/package.json +3 -3
  2. package/src/augmentation.d.ts +86 -86
  3. package/src/augmentation.js +5 -5
  4. package/src/bindings/addkeyboardhandlingforgrid.d.ts +27 -27
  5. package/src/bindings/addkeyboardhandlingforgrid.js +107 -107
  6. package/src/bindings/clickoutsidehandler.d.ts +28 -28
  7. package/src/bindings/clickoutsidehandler.js +36 -36
  8. package/src/bindings/csstransitiondisablermixin.d.ts +40 -40
  9. package/src/bindings/csstransitiondisablermixin.js +55 -55
  10. package/src/bindings/injectcsstransitiondisabler.d.ts +59 -59
  11. package/src/bindings/injectcsstransitiondisabler.js +71 -71
  12. package/src/bindings/preventdefault.d.ts +33 -33
  13. package/src/bindings/preventdefault.js +34 -34
  14. package/src/bindings/submithandler.d.ts +57 -57
  15. package/src/bindings/submithandler.js +47 -47
  16. package/src/button/button.d.ts +178 -178
  17. package/src/button/button.js +5 -5
  18. package/src/button/buttonview.d.ts +177 -177
  19. package/src/button/buttonview.js +231 -231
  20. package/src/button/switchbuttonview.d.ts +45 -45
  21. package/src/button/switchbuttonview.js +75 -75
  22. package/src/colorgrid/colorgridview.d.ts +132 -132
  23. package/src/colorgrid/colorgridview.js +124 -124
  24. package/src/colorgrid/colortileview.d.ts +28 -28
  25. package/src/colorgrid/colortileview.js +40 -40
  26. package/src/colorgrid/utils.d.ts +47 -47
  27. package/src/colorgrid/utils.js +84 -84
  28. package/src/colorpicker/colorpickerview.d.ts +110 -110
  29. package/src/colorpicker/colorpickerview.js +253 -253
  30. package/src/colorpicker/utils.d.ts +35 -35
  31. package/src/colorpicker/utils.js +99 -99
  32. package/src/componentfactory.d.ts +81 -81
  33. package/src/componentfactory.js +104 -104
  34. package/src/dropdown/button/dropdownbutton.d.ts +25 -25
  35. package/src/dropdown/button/dropdownbutton.js +5 -5
  36. package/src/dropdown/button/dropdownbuttonview.d.ts +48 -48
  37. package/src/dropdown/button/dropdownbuttonview.js +66 -66
  38. package/src/dropdown/button/splitbuttonview.d.ts +161 -161
  39. package/src/dropdown/button/splitbuttonview.js +152 -152
  40. package/src/dropdown/dropdownpanelfocusable.d.ts +21 -21
  41. package/src/dropdown/dropdownpanelfocusable.js +5 -5
  42. package/src/dropdown/dropdownpanelview.d.ts +62 -62
  43. package/src/dropdown/dropdownpanelview.js +96 -96
  44. package/src/dropdown/dropdownview.d.ts +315 -315
  45. package/src/dropdown/dropdownview.js +378 -378
  46. package/src/dropdown/utils.d.ts +221 -221
  47. package/src/dropdown/utils.js +434 -434
  48. package/src/editableui/editableuiview.d.ts +72 -72
  49. package/src/editableui/editableuiview.js +112 -112
  50. package/src/editableui/inline/inlineeditableuiview.d.ts +40 -40
  51. package/src/editableui/inline/inlineeditableuiview.js +48 -48
  52. package/src/editorui/bodycollection.d.ts +55 -55
  53. package/src/editorui/bodycollection.js +84 -84
  54. package/src/editorui/boxed/boxededitoruiview.d.ts +40 -40
  55. package/src/editorui/boxed/boxededitoruiview.js +81 -81
  56. package/src/editorui/editorui.d.ts +282 -282
  57. package/src/editorui/editorui.js +410 -410
  58. package/src/editorui/editoruiview.d.ts +39 -39
  59. package/src/editorui/editoruiview.js +38 -38
  60. package/src/editorui/poweredby.d.ts +71 -71
  61. package/src/editorui/poweredby.js +294 -294
  62. package/src/focuscycler.d.ts +183 -183
  63. package/src/focuscycler.js +220 -220
  64. package/src/formheader/formheaderview.d.ts +53 -53
  65. package/src/formheader/formheaderview.js +63 -63
  66. package/src/icon/iconview.d.ts +78 -78
  67. package/src/icon/iconview.js +112 -112
  68. package/src/iframe/iframeview.d.ts +50 -50
  69. package/src/iframe/iframeview.js +63 -63
  70. package/src/index.d.ts +62 -62
  71. package/src/index.js +61 -61
  72. package/src/input/inputview.d.ts +121 -121
  73. package/src/input/inputview.js +106 -106
  74. package/src/inputnumber/inputnumberview.d.ts +49 -49
  75. package/src/inputnumber/inputnumberview.js +40 -40
  76. package/src/inputtext/inputtextview.d.ts +18 -18
  77. package/src/inputtext/inputtextview.js +27 -27
  78. package/src/label/labelview.d.ts +36 -36
  79. package/src/label/labelview.js +41 -41
  80. package/src/labeledfield/labeledfieldview.d.ts +182 -182
  81. package/src/labeledfield/labeledfieldview.js +157 -157
  82. package/src/labeledfield/utils.d.ts +93 -93
  83. package/src/labeledfield/utils.js +131 -131
  84. package/src/labeledinput/labeledinputview.d.ts +125 -125
  85. package/src/labeledinput/labeledinputview.js +125 -125
  86. package/src/list/listitemview.d.ts +35 -35
  87. package/src/list/listitemview.js +40 -40
  88. package/src/list/listseparatorview.d.ts +18 -18
  89. package/src/list/listseparatorview.js +28 -28
  90. package/src/list/listview.d.ts +65 -65
  91. package/src/list/listview.js +90 -90
  92. package/src/model.d.ts +22 -22
  93. package/src/model.js +31 -31
  94. package/src/notification/notification.d.ts +211 -211
  95. package/src/notification/notification.js +187 -187
  96. package/src/panel/balloon/balloonpanelview.d.ts +685 -685
  97. package/src/panel/balloon/balloonpanelview.js +988 -988
  98. package/src/panel/balloon/contextualballoon.d.ts +299 -299
  99. package/src/panel/balloon/contextualballoon.js +572 -572
  100. package/src/panel/sticky/stickypanelview.d.ts +132 -132
  101. package/src/panel/sticky/stickypanelview.js +139 -139
  102. package/src/template.d.ts +942 -942
  103. package/src/template.js +1294 -1294
  104. package/src/toolbar/balloon/balloontoolbar.d.ts +122 -122
  105. package/src/toolbar/balloon/balloontoolbar.js +300 -300
  106. package/src/toolbar/block/blockbuttonview.d.ts +35 -35
  107. package/src/toolbar/block/blockbuttonview.js +41 -41
  108. package/src/toolbar/block/blocktoolbar.d.ts +161 -161
  109. package/src/toolbar/block/blocktoolbar.js +391 -391
  110. package/src/toolbar/normalizetoolbarconfig.d.ts +39 -39
  111. package/src/toolbar/normalizetoolbarconfig.js +51 -51
  112. package/src/toolbar/toolbarlinebreakview.d.ts +18 -18
  113. package/src/toolbar/toolbarlinebreakview.js +28 -28
  114. package/src/toolbar/toolbarseparatorview.d.ts +18 -18
  115. package/src/toolbar/toolbarseparatorview.js +28 -28
  116. package/src/toolbar/toolbarview.d.ts +265 -265
  117. package/src/toolbar/toolbarview.js +717 -717
  118. package/src/tooltipmanager.d.ts +180 -180
  119. package/src/tooltipmanager.js +353 -353
  120. package/src/view.d.ts +422 -422
  121. package/src/view.js +396 -396
  122. package/src/viewcollection.d.ts +139 -139
  123. package/src/viewcollection.js +206 -206
@@ -1,294 +1,294 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- import { Rect, DomEmitterMixin, findClosestScrollableAncestor, verifyLicense } from '@ckeditor/ckeditor5-utils';
6
- import BalloonPanelView from '../panel/balloon/balloonpanelview';
7
- import IconView from '../icon/iconview';
8
- import View from '../view';
9
- import { throttle } from 'lodash-es';
10
- import poweredByIcon from '../../theme/icons/project-logo.svg';
11
- const ICON_WIDTH = 53;
12
- const ICON_HEIGHT = 10;
13
- // ⚠ Note, whenever changing the threshold, make sure to update the docs/support/managing-ckeditor-logo.md docs
14
- // as this information is also mentioned there ⚠.
15
- const NARROW_ROOT_HEIGHT_THRESHOLD = 50;
16
- const NARROW_ROOT_WIDTH_THRESHOLD = 350;
17
- const DEFAULT_LABEL = 'Powered by';
18
- const OFF_THE_SCREEN_POSITION = {
19
- top: -99999,
20
- left: -99999,
21
- name: 'invalid',
22
- config: {
23
- withArrow: false
24
- }
25
- };
26
- /**
27
- * A helper that enables the "powered by" feature in the editor and renders a link to the project's
28
- * webpage next to the bottom of the editable element (editor root, source editing area, etc.) when the editor is focused.
29
- *
30
- * @private
31
- */
32
- export default class PoweredBy extends DomEmitterMixin() {
33
- /**
34
- * Creates a "powered by" helper for a given editor. The feature is initialized on Editor#ready
35
- * event.
36
- *
37
- * @param editor
38
- */
39
- constructor(editor) {
40
- super();
41
- this.editor = editor;
42
- this._balloonView = null;
43
- this._lastFocusedEditableElement = null;
44
- this._showBalloonThrottled = throttle(this._showBalloon.bind(this), 50, { leading: true });
45
- editor.on('ready', this._handleEditorReady.bind(this));
46
- }
47
- /**
48
- * Destroys the "powered by" helper along with its view.
49
- */
50
- destroy() {
51
- const balloon = this._balloonView;
52
- if (balloon) {
53
- // Balloon gets destroyed by the body collection.
54
- // The powered by view gets destroyed by the balloon.
55
- balloon.unpin();
56
- this._balloonView = null;
57
- }
58
- this._showBalloonThrottled.cancel();
59
- this.stopListening();
60
- }
61
- /**
62
- * Enables "powered by" label once the editor (ui) is ready.
63
- */
64
- _handleEditorReady() {
65
- const editor = this.editor;
66
- const forceVisible = !!editor.config.get('ui.poweredBy.forceVisible');
67
- /* istanbul ignore next -- @preserve */
68
- if (!forceVisible && verifyLicense(editor.config.get('licenseKey')) === 'VALID') {
69
- return;
70
- }
71
- // No view means no body collection to append the powered by balloon to.
72
- if (!editor.ui.view) {
73
- return;
74
- }
75
- editor.ui.focusTracker.on('change:isFocused', (evt, data, isFocused) => {
76
- this._updateLastFocusedEditableElement();
77
- if (isFocused) {
78
- this._showBalloon();
79
- }
80
- else {
81
- this._hideBalloon();
82
- }
83
- });
84
- editor.ui.focusTracker.on('change:focusedElement', (evt, data, focusedElement) => {
85
- this._updateLastFocusedEditableElement();
86
- if (focusedElement) {
87
- this._showBalloon();
88
- }
89
- });
90
- editor.ui.on('update', () => {
91
- this._showBalloonThrottled();
92
- });
93
- }
94
- /**
95
- * Creates an instance of the {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView balloon panel}
96
- * with the "powered by" view inside ready for positioning.
97
- */
98
- _createBalloonView() {
99
- const editor = this.editor;
100
- const balloon = this._balloonView = new BalloonPanelView();
101
- const poweredByConfig = getNormalizedConfig(editor);
102
- const view = new PoweredByView(editor.locale, poweredByConfig.label);
103
- balloon.content.add(view);
104
- balloon.set({
105
- class: 'ck-powered-by-balloon'
106
- });
107
- editor.ui.view.body.add(balloon);
108
- editor.ui.focusTracker.add(balloon.element);
109
- this._balloonView = balloon;
110
- }
111
- /**
112
- * Attempts to display the balloon with the "powered by" view.
113
- */
114
- _showBalloon() {
115
- if (!this._lastFocusedEditableElement) {
116
- return;
117
- }
118
- const attachOptions = getBalloonAttachOptions(this.editor, this._lastFocusedEditableElement);
119
- if (attachOptions) {
120
- if (!this._balloonView) {
121
- this._createBalloonView();
122
- }
123
- this._balloonView.pin(attachOptions);
124
- }
125
- }
126
- /**
127
- * Hides the "powered by" balloon if already visible.
128
- */
129
- _hideBalloon() {
130
- if (this._balloonView) {
131
- this._balloonView.unpin();
132
- }
133
- }
134
- /**
135
- * Updates the {@link #_lastFocusedEditableElement} based on the state of the global focus tracker.
136
- */
137
- _updateLastFocusedEditableElement() {
138
- const editor = this.editor;
139
- const isFocused = editor.ui.focusTracker.isFocused;
140
- const focusedElement = editor.ui.focusTracker.focusedElement;
141
- if (!isFocused || !focusedElement) {
142
- this._lastFocusedEditableElement = null;
143
- return;
144
- }
145
- const editableEditorElements = Array.from(editor.ui.getEditableElementsNames()).map(name => {
146
- return editor.ui.getEditableElement(name);
147
- });
148
- if (editableEditorElements.includes(focusedElement)) {
149
- this._lastFocusedEditableElement = focusedElement;
150
- }
151
- else {
152
- // If it's none of the editable element, then the focus is somewhere in the UI. Let's display powered by
153
- // over the first element then.
154
- this._lastFocusedEditableElement = editableEditorElements[0];
155
- }
156
- }
157
- }
158
- /**
159
- * A view displaying a "powered by" label and project logo wrapped in a link.
160
- */
161
- class PoweredByView extends View {
162
- /**
163
- * Created an instance of the "powered by" view.
164
- *
165
- * @param locale The localization services instance.
166
- * @param label The label text.
167
- */
168
- constructor(locale, label) {
169
- super(locale);
170
- const iconView = new IconView();
171
- const bind = this.bindTemplate;
172
- iconView.set({
173
- content: poweredByIcon,
174
- isColorInherited: false
175
- });
176
- iconView.extendTemplate({
177
- attributes: {
178
- style: {
179
- width: ICON_WIDTH + 'px',
180
- height: ICON_HEIGHT + 'px'
181
- }
182
- }
183
- });
184
- this.setTemplate({
185
- tag: 'div',
186
- attributes: {
187
- class: ['ck', 'ck-powered-by'],
188
- 'aria-hidden': true
189
- },
190
- children: [
191
- {
192
- tag: 'a',
193
- attributes: {
194
- href: 'https://ckeditor.com/?utm_source=ckeditor&' +
195
- 'utm_medium=referral&utm_campaign=701Dn000000hVgmIAE_powered_by_ckeditor_logo',
196
- target: '_blank',
197
- tabindex: '-1'
198
- },
199
- children: [
200
- ...label ? [
201
- {
202
- tag: 'span',
203
- attributes: {
204
- class: ['ck', 'ck-powered-by__label']
205
- },
206
- children: [label]
207
- }
208
- ] : [],
209
- iconView
210
- ],
211
- on: {
212
- dragstart: bind.to(evt => evt.preventDefault())
213
- }
214
- }
215
- ]
216
- });
217
- }
218
- }
219
- function getBalloonAttachOptions(editor, focusedEditableElement) {
220
- const poweredByConfig = getNormalizedConfig(editor);
221
- const positioningFunction = poweredByConfig.side === 'right' ?
222
- getLowerRightCornerPosition(focusedEditableElement, poweredByConfig) :
223
- getLowerLeftCornerPosition(focusedEditableElement, poweredByConfig);
224
- return {
225
- target: focusedEditableElement,
226
- positions: [positioningFunction]
227
- };
228
- }
229
- function getLowerRightCornerPosition(focusedEditableElement, config) {
230
- return getLowerCornerPosition(focusedEditableElement, config, (rootRect, balloonRect) => {
231
- return rootRect.left + rootRect.width - balloonRect.width - config.horizontalOffset;
232
- });
233
- }
234
- function getLowerLeftCornerPosition(focusedEditableElement, config) {
235
- return getLowerCornerPosition(focusedEditableElement, config, rootRect => rootRect.left + config.horizontalOffset);
236
- }
237
- function getLowerCornerPosition(focusedEditableElement, config, getBalloonLeft) {
238
- return (editableElementRect, balloonRect) => {
239
- const visibleEditableElementRect = editableElementRect.getVisible();
240
- // Root cropped by ancestors.
241
- if (!visibleEditableElementRect) {
242
- return OFF_THE_SCREEN_POSITION;
243
- }
244
- if (editableElementRect.width < NARROW_ROOT_WIDTH_THRESHOLD || editableElementRect.height < NARROW_ROOT_HEIGHT_THRESHOLD) {
245
- return OFF_THE_SCREEN_POSITION;
246
- }
247
- let balloonTop;
248
- if (config.position === 'inside') {
249
- balloonTop = editableElementRect.bottom - balloonRect.height;
250
- }
251
- else {
252
- balloonTop = editableElementRect.bottom - balloonRect.height / 2;
253
- }
254
- balloonTop -= config.verticalOffset;
255
- const balloonLeft = getBalloonLeft(editableElementRect, balloonRect);
256
- if (config.position === 'inside') {
257
- const newBalloonRect = balloonRect.clone().moveTo(balloonLeft, balloonTop);
258
- // The watermark cannot be positioned in this corner because the corner is not quite visible.
259
- if (newBalloonRect.getIntersectionArea(visibleEditableElementRect) < newBalloonRect.getArea()) {
260
- return OFF_THE_SCREEN_POSITION;
261
- }
262
- }
263
- else {
264
- const firstScrollableEditableElementAncestor = findClosestScrollableAncestor(focusedEditableElement);
265
- if (firstScrollableEditableElementAncestor) {
266
- const firstScrollableEditableElementAncestorRect = new Rect(firstScrollableEditableElementAncestor);
267
- // The watermark cannot be positioned in this corner because the corner is "not visible enough".
268
- if (visibleEditableElementRect.bottom + balloonRect.height / 2 > firstScrollableEditableElementAncestorRect.bottom) {
269
- return OFF_THE_SCREEN_POSITION;
270
- }
271
- }
272
- }
273
- return {
274
- top: balloonTop,
275
- left: balloonLeft,
276
- name: `position_${config.position}-side_${config.side}`,
277
- config: {
278
- withArrow: false
279
- }
280
- };
281
- };
282
- }
283
- function getNormalizedConfig(editor) {
284
- const userConfig = editor.config.get('ui.poweredBy');
285
- const position = userConfig && userConfig.position || 'border';
286
- return {
287
- position,
288
- label: DEFAULT_LABEL,
289
- verticalOffset: position === 'inside' ? 5 : 0,
290
- horizontalOffset: 5,
291
- side: editor.locale.contentLanguageDirection === 'ltr' ? 'right' : 'left',
292
- ...userConfig
293
- };
294
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ import { Rect, DomEmitterMixin, findClosestScrollableAncestor, verifyLicense } from '@ckeditor/ckeditor5-utils';
6
+ import BalloonPanelView from '../panel/balloon/balloonpanelview';
7
+ import IconView from '../icon/iconview';
8
+ import View from '../view';
9
+ import { throttle } from 'lodash-es';
10
+ import poweredByIcon from '../../theme/icons/project-logo.svg';
11
+ const ICON_WIDTH = 53;
12
+ const ICON_HEIGHT = 10;
13
+ // ⚠ Note, whenever changing the threshold, make sure to update the docs/support/managing-ckeditor-logo.md docs
14
+ // as this information is also mentioned there ⚠.
15
+ const NARROW_ROOT_HEIGHT_THRESHOLD = 50;
16
+ const NARROW_ROOT_WIDTH_THRESHOLD = 350;
17
+ const DEFAULT_LABEL = 'Powered by';
18
+ const OFF_THE_SCREEN_POSITION = {
19
+ top: -99999,
20
+ left: -99999,
21
+ name: 'invalid',
22
+ config: {
23
+ withArrow: false
24
+ }
25
+ };
26
+ /**
27
+ * A helper that enables the "powered by" feature in the editor and renders a link to the project's
28
+ * webpage next to the bottom of the editable element (editor root, source editing area, etc.) when the editor is focused.
29
+ *
30
+ * @private
31
+ */
32
+ export default class PoweredBy extends DomEmitterMixin() {
33
+ /**
34
+ * Creates a "powered by" helper for a given editor. The feature is initialized on Editor#ready
35
+ * event.
36
+ *
37
+ * @param editor
38
+ */
39
+ constructor(editor) {
40
+ super();
41
+ this.editor = editor;
42
+ this._balloonView = null;
43
+ this._lastFocusedEditableElement = null;
44
+ this._showBalloonThrottled = throttle(this._showBalloon.bind(this), 50, { leading: true });
45
+ editor.on('ready', this._handleEditorReady.bind(this));
46
+ }
47
+ /**
48
+ * Destroys the "powered by" helper along with its view.
49
+ */
50
+ destroy() {
51
+ const balloon = this._balloonView;
52
+ if (balloon) {
53
+ // Balloon gets destroyed by the body collection.
54
+ // The powered by view gets destroyed by the balloon.
55
+ balloon.unpin();
56
+ this._balloonView = null;
57
+ }
58
+ this._showBalloonThrottled.cancel();
59
+ this.stopListening();
60
+ }
61
+ /**
62
+ * Enables "powered by" label once the editor (ui) is ready.
63
+ */
64
+ _handleEditorReady() {
65
+ const editor = this.editor;
66
+ const forceVisible = !!editor.config.get('ui.poweredBy.forceVisible');
67
+ /* istanbul ignore next -- @preserve */
68
+ if (!forceVisible && verifyLicense(editor.config.get('licenseKey')) === 'VALID') {
69
+ return;
70
+ }
71
+ // No view means no body collection to append the powered by balloon to.
72
+ if (!editor.ui.view) {
73
+ return;
74
+ }
75
+ editor.ui.focusTracker.on('change:isFocused', (evt, data, isFocused) => {
76
+ this._updateLastFocusedEditableElement();
77
+ if (isFocused) {
78
+ this._showBalloon();
79
+ }
80
+ else {
81
+ this._hideBalloon();
82
+ }
83
+ });
84
+ editor.ui.focusTracker.on('change:focusedElement', (evt, data, focusedElement) => {
85
+ this._updateLastFocusedEditableElement();
86
+ if (focusedElement) {
87
+ this._showBalloon();
88
+ }
89
+ });
90
+ editor.ui.on('update', () => {
91
+ this._showBalloonThrottled();
92
+ });
93
+ }
94
+ /**
95
+ * Creates an instance of the {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView balloon panel}
96
+ * with the "powered by" view inside ready for positioning.
97
+ */
98
+ _createBalloonView() {
99
+ const editor = this.editor;
100
+ const balloon = this._balloonView = new BalloonPanelView();
101
+ const poweredByConfig = getNormalizedConfig(editor);
102
+ const view = new PoweredByView(editor.locale, poweredByConfig.label);
103
+ balloon.content.add(view);
104
+ balloon.set({
105
+ class: 'ck-powered-by-balloon'
106
+ });
107
+ editor.ui.view.body.add(balloon);
108
+ editor.ui.focusTracker.add(balloon.element);
109
+ this._balloonView = balloon;
110
+ }
111
+ /**
112
+ * Attempts to display the balloon with the "powered by" view.
113
+ */
114
+ _showBalloon() {
115
+ if (!this._lastFocusedEditableElement) {
116
+ return;
117
+ }
118
+ const attachOptions = getBalloonAttachOptions(this.editor, this._lastFocusedEditableElement);
119
+ if (attachOptions) {
120
+ if (!this._balloonView) {
121
+ this._createBalloonView();
122
+ }
123
+ this._balloonView.pin(attachOptions);
124
+ }
125
+ }
126
+ /**
127
+ * Hides the "powered by" balloon if already visible.
128
+ */
129
+ _hideBalloon() {
130
+ if (this._balloonView) {
131
+ this._balloonView.unpin();
132
+ }
133
+ }
134
+ /**
135
+ * Updates the {@link #_lastFocusedEditableElement} based on the state of the global focus tracker.
136
+ */
137
+ _updateLastFocusedEditableElement() {
138
+ const editor = this.editor;
139
+ const isFocused = editor.ui.focusTracker.isFocused;
140
+ const focusedElement = editor.ui.focusTracker.focusedElement;
141
+ if (!isFocused || !focusedElement) {
142
+ this._lastFocusedEditableElement = null;
143
+ return;
144
+ }
145
+ const editableEditorElements = Array.from(editor.ui.getEditableElementsNames()).map(name => {
146
+ return editor.ui.getEditableElement(name);
147
+ });
148
+ if (editableEditorElements.includes(focusedElement)) {
149
+ this._lastFocusedEditableElement = focusedElement;
150
+ }
151
+ else {
152
+ // If it's none of the editable element, then the focus is somewhere in the UI. Let's display powered by
153
+ // over the first element then.
154
+ this._lastFocusedEditableElement = editableEditorElements[0];
155
+ }
156
+ }
157
+ }
158
+ /**
159
+ * A view displaying a "powered by" label and project logo wrapped in a link.
160
+ */
161
+ class PoweredByView extends View {
162
+ /**
163
+ * Created an instance of the "powered by" view.
164
+ *
165
+ * @param locale The localization services instance.
166
+ * @param label The label text.
167
+ */
168
+ constructor(locale, label) {
169
+ super(locale);
170
+ const iconView = new IconView();
171
+ const bind = this.bindTemplate;
172
+ iconView.set({
173
+ content: poweredByIcon,
174
+ isColorInherited: false
175
+ });
176
+ iconView.extendTemplate({
177
+ attributes: {
178
+ style: {
179
+ width: ICON_WIDTH + 'px',
180
+ height: ICON_HEIGHT + 'px'
181
+ }
182
+ }
183
+ });
184
+ this.setTemplate({
185
+ tag: 'div',
186
+ attributes: {
187
+ class: ['ck', 'ck-powered-by'],
188
+ 'aria-hidden': true
189
+ },
190
+ children: [
191
+ {
192
+ tag: 'a',
193
+ attributes: {
194
+ href: 'https://ckeditor.com/?utm_source=ckeditor&' +
195
+ 'utm_medium=referral&utm_campaign=701Dn000000hVgmIAE_powered_by_ckeditor_logo',
196
+ target: '_blank',
197
+ tabindex: '-1'
198
+ },
199
+ children: [
200
+ ...label ? [
201
+ {
202
+ tag: 'span',
203
+ attributes: {
204
+ class: ['ck', 'ck-powered-by__label']
205
+ },
206
+ children: [label]
207
+ }
208
+ ] : [],
209
+ iconView
210
+ ],
211
+ on: {
212
+ dragstart: bind.to(evt => evt.preventDefault())
213
+ }
214
+ }
215
+ ]
216
+ });
217
+ }
218
+ }
219
+ function getBalloonAttachOptions(editor, focusedEditableElement) {
220
+ const poweredByConfig = getNormalizedConfig(editor);
221
+ const positioningFunction = poweredByConfig.side === 'right' ?
222
+ getLowerRightCornerPosition(focusedEditableElement, poweredByConfig) :
223
+ getLowerLeftCornerPosition(focusedEditableElement, poweredByConfig);
224
+ return {
225
+ target: focusedEditableElement,
226
+ positions: [positioningFunction]
227
+ };
228
+ }
229
+ function getLowerRightCornerPosition(focusedEditableElement, config) {
230
+ return getLowerCornerPosition(focusedEditableElement, config, (rootRect, balloonRect) => {
231
+ return rootRect.left + rootRect.width - balloonRect.width - config.horizontalOffset;
232
+ });
233
+ }
234
+ function getLowerLeftCornerPosition(focusedEditableElement, config) {
235
+ return getLowerCornerPosition(focusedEditableElement, config, rootRect => rootRect.left + config.horizontalOffset);
236
+ }
237
+ function getLowerCornerPosition(focusedEditableElement, config, getBalloonLeft) {
238
+ return (editableElementRect, balloonRect) => {
239
+ const visibleEditableElementRect = editableElementRect.getVisible();
240
+ // Root cropped by ancestors.
241
+ if (!visibleEditableElementRect) {
242
+ return OFF_THE_SCREEN_POSITION;
243
+ }
244
+ if (editableElementRect.width < NARROW_ROOT_WIDTH_THRESHOLD || editableElementRect.height < NARROW_ROOT_HEIGHT_THRESHOLD) {
245
+ return OFF_THE_SCREEN_POSITION;
246
+ }
247
+ let balloonTop;
248
+ if (config.position === 'inside') {
249
+ balloonTop = editableElementRect.bottom - balloonRect.height;
250
+ }
251
+ else {
252
+ balloonTop = editableElementRect.bottom - balloonRect.height / 2;
253
+ }
254
+ balloonTop -= config.verticalOffset;
255
+ const balloonLeft = getBalloonLeft(editableElementRect, balloonRect);
256
+ if (config.position === 'inside') {
257
+ const newBalloonRect = balloonRect.clone().moveTo(balloonLeft, balloonTop);
258
+ // The watermark cannot be positioned in this corner because the corner is not quite visible.
259
+ if (newBalloonRect.getIntersectionArea(visibleEditableElementRect) < newBalloonRect.getArea()) {
260
+ return OFF_THE_SCREEN_POSITION;
261
+ }
262
+ }
263
+ else {
264
+ const firstScrollableEditableElementAncestor = findClosestScrollableAncestor(focusedEditableElement);
265
+ if (firstScrollableEditableElementAncestor) {
266
+ const firstScrollableEditableElementAncestorRect = new Rect(firstScrollableEditableElementAncestor);
267
+ // The watermark cannot be positioned in this corner because the corner is "not visible enough".
268
+ if (visibleEditableElementRect.bottom + balloonRect.height / 2 > firstScrollableEditableElementAncestorRect.bottom) {
269
+ return OFF_THE_SCREEN_POSITION;
270
+ }
271
+ }
272
+ }
273
+ return {
274
+ top: balloonTop,
275
+ left: balloonLeft,
276
+ name: `position_${config.position}-side_${config.side}`,
277
+ config: {
278
+ withArrow: false
279
+ }
280
+ };
281
+ };
282
+ }
283
+ function getNormalizedConfig(editor) {
284
+ const userConfig = editor.config.get('ui.poweredBy');
285
+ const position = userConfig && userConfig.position || 'border';
286
+ return {
287
+ position,
288
+ label: DEFAULT_LABEL,
289
+ verticalOffset: position === 'inside' ? 5 : 0,
290
+ horizontalOffset: 5,
291
+ side: editor.locale.contentLanguageDirection === 'ltr' ? 'right' : 'left',
292
+ ...userConfig
293
+ };
294
+ }