@ckeditor/ckeditor5-ui 37.1.0 → 38.0.0-rc.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 (91) hide show
  1. package/lang/contexts.json +2 -1
  2. package/lang/translations/ar.po +5 -1
  3. package/lang/translations/ast.po +5 -1
  4. package/lang/translations/az.po +5 -1
  5. package/lang/translations/bg.po +5 -1
  6. package/lang/translations/bn.po +5 -1
  7. package/lang/translations/ca.po +5 -1
  8. package/lang/translations/cs.po +5 -1
  9. package/lang/translations/da.po +5 -1
  10. package/lang/translations/de-ch.po +5 -1
  11. package/lang/translations/de.po +5 -1
  12. package/lang/translations/el.po +5 -1
  13. package/lang/translations/en-au.po +6 -2
  14. package/lang/translations/en-gb.po +5 -1
  15. package/lang/translations/en.po +4 -0
  16. package/lang/translations/eo.po +5 -1
  17. package/lang/translations/es.po +5 -1
  18. package/lang/translations/et.po +5 -1
  19. package/lang/translations/eu.po +5 -1
  20. package/lang/translations/fa.po +5 -1
  21. package/lang/translations/fi.po +5 -1
  22. package/lang/translations/fr.po +5 -1
  23. package/lang/translations/gl.po +5 -1
  24. package/lang/translations/he.po +5 -1
  25. package/lang/translations/hi.po +5 -1
  26. package/lang/translations/hr.po +6 -2
  27. package/lang/translations/hu.po +5 -1
  28. package/lang/translations/id.po +5 -1
  29. package/lang/translations/it.po +5 -1
  30. package/lang/translations/ja.po +5 -1
  31. package/lang/translations/km.po +5 -1
  32. package/lang/translations/kn.po +5 -1
  33. package/lang/translations/ko.po +5 -1
  34. package/lang/translations/ku.po +5 -1
  35. package/lang/translations/lt.po +5 -1
  36. package/lang/translations/lv.po +5 -1
  37. package/lang/translations/ms.po +5 -1
  38. package/lang/translations/nb.po +5 -1
  39. package/lang/translations/ne.po +5 -1
  40. package/lang/translations/nl.po +5 -1
  41. package/lang/translations/no.po +5 -1
  42. package/lang/translations/pl.po +5 -1
  43. package/lang/translations/pt-br.po +5 -1
  44. package/lang/translations/pt.po +5 -1
  45. package/lang/translations/ro.po +5 -1
  46. package/lang/translations/ru.po +5 -1
  47. package/lang/translations/sk.po +5 -1
  48. package/lang/translations/sl.po +5 -1
  49. package/lang/translations/sq.po +5 -1
  50. package/lang/translations/sr-latn.po +5 -1
  51. package/lang/translations/sr.po +5 -1
  52. package/lang/translations/sv.po +5 -1
  53. package/lang/translations/th.po +5 -1
  54. package/lang/translations/tk.po +5 -1
  55. package/lang/translations/tr.po +5 -1
  56. package/lang/translations/tt.po +5 -1
  57. package/lang/translations/ug.po +5 -1
  58. package/lang/translations/uk.po +5 -1
  59. package/lang/translations/ur.po +5 -1
  60. package/lang/translations/uz.po +5 -1
  61. package/lang/translations/vi.po +5 -1
  62. package/lang/translations/zh-cn.po +5 -1
  63. package/lang/translations/zh.po +6 -2
  64. package/package.json +27 -22
  65. package/src/button/button.d.ts +24 -0
  66. package/src/button/buttonview.d.ts +24 -2
  67. package/src/button/buttonview.js +33 -10
  68. package/src/colorpicker/colorpickerview.d.ts +110 -0
  69. package/src/colorpicker/colorpickerview.js +250 -0
  70. package/src/colorpicker/utils.d.ts +35 -0
  71. package/src/colorpicker/utils.js +99 -0
  72. package/src/dropdown/button/splitbuttonview.d.ts +16 -0
  73. package/src/dropdown/dropdownpanelview.js +6 -1
  74. package/src/dropdown/utils.d.ts +2 -0
  75. package/src/dropdown/utils.js +2 -0
  76. package/src/editorui/editorui.d.ts +5 -0
  77. package/src/editorui/editorui.js +3 -0
  78. package/src/editorui/poweredby.d.ts +71 -0
  79. package/src/editorui/poweredby.js +285 -0
  80. package/src/index.d.ts +3 -0
  81. package/src/index.js +2 -0
  82. package/src/list/listitemview.js +2 -1
  83. package/src/list/listview.d.ts +6 -0
  84. package/src/list/listview.js +2 -0
  85. package/src/toolbar/block/blocktoolbar.js +3 -25
  86. package/src/toolbar/toolbarview.js +3 -1
  87. package/theme/components/colorpicker/colorpicker.css +24 -0
  88. package/theme/globals/_poweredby.css +67 -0
  89. package/theme/globals/globals.css +1 -0
  90. package/theme/icons/color-palette.svg +1 -0
  91. package/theme/icons/project-logo.svg +1 -0
@@ -8,6 +8,7 @@
8
8
  /* globals console */
9
9
  import ComponentFactory from '../componentfactory';
10
10
  import TooltipManager from '../tooltipmanager';
11
+ import PoweredBy from './poweredby';
11
12
  import { ObservableMixin, isVisible, FocusTracker } from '@ckeditor/ckeditor5-utils';
12
13
  /**
13
14
  * A class providing the minimal interface that is required to successfully bootstrap any editor UI.
@@ -39,6 +40,7 @@ export default class EditorUI extends ObservableMixin() {
39
40
  this.componentFactory = new ComponentFactory(editor);
40
41
  this.focusTracker = new FocusTracker();
41
42
  this.tooltipManager = new TooltipManager(editor);
43
+ this.poweredBy = new PoweredBy(editor);
42
44
  this.set('viewportOffset', this._readViewportOffsetFromConfig());
43
45
  this.once('ready', () => {
44
46
  this.isReady = true;
@@ -78,6 +80,7 @@ export default class EditorUI extends ObservableMixin() {
78
80
  this.stopListening();
79
81
  this.focusTracker.destroy();
80
82
  this.tooltipManager.destroy(this.editor);
83
+ this.poweredBy.destroy();
81
84
  // Clean–up the references to the CKEditor instance stored in the native editable DOM elements.
82
85
  for (const domElement of this._editableElementsMap.values()) {
83
86
  domElement.ckeditorInstance = null;
@@ -0,0 +1,71 @@
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
+ /**
6
+ * @module ui/editorui/poweredby
7
+ */
8
+ import type { Editor } from '@ckeditor/ckeditor5-core';
9
+ declare const PoweredBy_base: {
10
+ new (): import("@ckeditor/ckeditor5-utils").DomEmitter;
11
+ prototype: import("@ckeditor/ckeditor5-utils").DomEmitter;
12
+ };
13
+ /**
14
+ * A helper that enables the "powered by" feature in the editor and renders a link to the project's
15
+ * webpage next to the bottom of the editable element (editor root, source editing area, etc.) when the editor is focused.
16
+ *
17
+ * @private
18
+ */
19
+ export default class PoweredBy extends PoweredBy_base {
20
+ /**
21
+ * Editor instance the helper was created for.
22
+ */
23
+ private readonly editor;
24
+ /**
25
+ * A reference to the balloon panel hosting and positioning the "powered by" link and logo.
26
+ */
27
+ private _balloonView;
28
+ /**
29
+ * A throttled version of the {@link #_showBalloon} method meant for frequent use to avoid performance loss.
30
+ */
31
+ private _showBalloonThrottled;
32
+ /**
33
+ * A reference to the last editable element (root, source editing area, etc.) focused by the user.
34
+ * Since the focus can move to other focusable elements in the UI, this reference allows positioning the balloon over the
35
+ * right element whether the user is typing or using the UI.
36
+ */
37
+ private _lastFocusedEditableElement;
38
+ /**
39
+ * Creates a "powered by" helper for a given editor. The feature is initialized on Editor#ready
40
+ * event.
41
+ *
42
+ * @param editor
43
+ */
44
+ constructor(editor: Editor);
45
+ /**
46
+ * Destroys the "powered by" helper along with its view.
47
+ */
48
+ destroy(): void;
49
+ /**
50
+ * Enables "powered by" label once the editor (ui) is ready.
51
+ */
52
+ private _handleEditorReady;
53
+ /**
54
+ * Creates an instance of the {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView balloon panel}
55
+ * with the "powered by" view inside ready for positioning.
56
+ */
57
+ private _createBalloonView;
58
+ /**
59
+ * Attempts to display the balloon with the "powered by" view.
60
+ */
61
+ private _showBalloon;
62
+ /**
63
+ * Hides the "powered by" balloon if already visible.
64
+ */
65
+ private _hideBalloon;
66
+ /**
67
+ * Updates the {@link #_lastFocusedEditableElement} based on the state of the global focus tracker.
68
+ */
69
+ private _updateLastFocusedEditableElement;
70
+ }
71
+ export {};
@@ -0,0 +1,285 @@
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
+ const NARROW_ROOT_HEIGHT_THRESHOLD = 50;
14
+ const NARROW_ROOT_WIDTH_THRESHOLD = 350;
15
+ const OFF_THE_SCREEN_POSITION = {
16
+ top: -99999,
17
+ left: -99999,
18
+ name: 'invalid',
19
+ config: {
20
+ withArrow: false
21
+ }
22
+ };
23
+ /**
24
+ * A helper that enables the "powered by" feature in the editor and renders a link to the project's
25
+ * webpage next to the bottom of the editable element (editor root, source editing area, etc.) when the editor is focused.
26
+ *
27
+ * @private
28
+ */
29
+ export default class PoweredBy extends DomEmitterMixin() {
30
+ /**
31
+ * Creates a "powered by" helper for a given editor. The feature is initialized on Editor#ready
32
+ * event.
33
+ *
34
+ * @param editor
35
+ */
36
+ constructor(editor) {
37
+ super();
38
+ this.editor = editor;
39
+ this._balloonView = null;
40
+ this._lastFocusedEditableElement = null;
41
+ this._showBalloonThrottled = throttle(this._showBalloon.bind(this), 50, { leading: true });
42
+ editor.on('ready', this._handleEditorReady.bind(this));
43
+ }
44
+ /**
45
+ * Destroys the "powered by" helper along with its view.
46
+ */
47
+ destroy() {
48
+ const balloon = this._balloonView;
49
+ if (balloon) {
50
+ // Balloon gets destroyed by the body collection.
51
+ // The powered by view gets destroyed by the balloon.
52
+ balloon.unpin();
53
+ this._balloonView = null;
54
+ }
55
+ this._showBalloonThrottled.cancel();
56
+ this.stopListening();
57
+ }
58
+ /**
59
+ * Enables "powered by" label once the editor (ui) is ready.
60
+ */
61
+ _handleEditorReady() {
62
+ const editor = this.editor;
63
+ /* istanbul ignore next -- @preserve */
64
+ if (verifyLicense(editor.config.get('licenseKey')) === 'VALID') {
65
+ return;
66
+ }
67
+ // No view means no body collection to append the powered by balloon to.
68
+ if (!editor.ui.view) {
69
+ return;
70
+ }
71
+ editor.ui.focusTracker.on('change:isFocused', (evt, data, isFocused) => {
72
+ this._updateLastFocusedEditableElement();
73
+ if (isFocused) {
74
+ this._showBalloon();
75
+ }
76
+ else {
77
+ this._hideBalloon();
78
+ }
79
+ });
80
+ editor.ui.focusTracker.on('change:focusedElement', (evt, data, focusedElement) => {
81
+ this._updateLastFocusedEditableElement();
82
+ if (focusedElement) {
83
+ this._showBalloon();
84
+ }
85
+ });
86
+ editor.ui.on('update', () => {
87
+ this._showBalloonThrottled();
88
+ });
89
+ }
90
+ /**
91
+ * Creates an instance of the {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView balloon panel}
92
+ * with the "powered by" view inside ready for positioning.
93
+ */
94
+ _createBalloonView() {
95
+ const editor = this.editor;
96
+ const balloon = this._balloonView = new BalloonPanelView();
97
+ const view = new PoweredByView(editor.locale);
98
+ balloon.content.add(view);
99
+ balloon.set({
100
+ class: 'ck-powered-by-balloon'
101
+ });
102
+ editor.ui.view.body.add(balloon);
103
+ editor.ui.focusTracker.add(balloon.element);
104
+ this._balloonView = balloon;
105
+ }
106
+ /**
107
+ * Attempts to display the balloon with the "powered by" view.
108
+ */
109
+ _showBalloon() {
110
+ if (!this._lastFocusedEditableElement) {
111
+ return;
112
+ }
113
+ const attachOptions = getBalloonAttachOptions(this.editor, this._lastFocusedEditableElement);
114
+ if (attachOptions) {
115
+ if (!this._balloonView) {
116
+ this._createBalloonView();
117
+ }
118
+ this._balloonView.pin(attachOptions);
119
+ }
120
+ }
121
+ /**
122
+ * Hides the "powered by" balloon if already visible.
123
+ */
124
+ _hideBalloon() {
125
+ if (this._balloonView) {
126
+ this._balloonView.unpin();
127
+ }
128
+ }
129
+ /**
130
+ * Updates the {@link #_lastFocusedEditableElement} based on the state of the global focus tracker.
131
+ */
132
+ _updateLastFocusedEditableElement() {
133
+ const editor = this.editor;
134
+ const isFocused = editor.ui.focusTracker.isFocused;
135
+ const focusedElement = editor.ui.focusTracker.focusedElement;
136
+ if (!isFocused || !focusedElement) {
137
+ this._lastFocusedEditableElement = null;
138
+ return;
139
+ }
140
+ const editableEditorElements = Array.from(editor.ui.getEditableElementsNames()).map(name => {
141
+ return editor.ui.getEditableElement(name);
142
+ });
143
+ if (editableEditorElements.includes(focusedElement)) {
144
+ this._lastFocusedEditableElement = focusedElement;
145
+ }
146
+ else {
147
+ // If it's none of the editable element, then the focus is somewhere in the UI. Let's display powered by
148
+ // over the first element then.
149
+ this._lastFocusedEditableElement = editableEditorElements[0];
150
+ }
151
+ }
152
+ }
153
+ /**
154
+ * A view displaying a "powered by" label and project logo wrapped in a link.
155
+ */
156
+ class PoweredByView extends View {
157
+ /**
158
+ * Created an instance of the "powered by" view.
159
+ *
160
+ * @param locale The localization services instance.
161
+ */
162
+ constructor(locale) {
163
+ super(locale);
164
+ const iconView = new IconView();
165
+ const bind = this.bindTemplate;
166
+ iconView.set({
167
+ content: poweredByIcon,
168
+ isColorInherited: false
169
+ });
170
+ iconView.extendTemplate({
171
+ attributes: {
172
+ style: {
173
+ width: ICON_WIDTH + 'px',
174
+ height: ICON_HEIGHT + 'px'
175
+ }
176
+ }
177
+ });
178
+ this.setTemplate({
179
+ tag: 'div',
180
+ attributes: {
181
+ class: ['ck', 'ck-powered-by'],
182
+ 'aria-hidden': true
183
+ },
184
+ children: [
185
+ {
186
+ tag: 'a',
187
+ attributes: {
188
+ href: 'https://ckeditor.com/?utm_source=ckeditor&' +
189
+ 'utm_medium=referral&utm_campaign=701Dn000000hVgmIAE_powered_by_ckeditor_logo',
190
+ target: '_blank',
191
+ tabindex: '-1'
192
+ },
193
+ children: [
194
+ {
195
+ tag: 'span',
196
+ attributes: {
197
+ class: ['ck', 'ck-powered-by__label']
198
+ },
199
+ children: ['Powered by']
200
+ },
201
+ iconView
202
+ ],
203
+ on: {
204
+ dragstart: bind.to(evt => evt.preventDefault())
205
+ }
206
+ }
207
+ ]
208
+ });
209
+ }
210
+ }
211
+ function getBalloonAttachOptions(editor, focusedEditableElement) {
212
+ const poweredByConfig = getNormalizedConfig(editor);
213
+ const positioningFunction = poweredByConfig.side === 'right' ?
214
+ getLowerRightCornerPosition(focusedEditableElement, poweredByConfig) :
215
+ getLowerLeftCornerPosition(focusedEditableElement, poweredByConfig);
216
+ return {
217
+ target: focusedEditableElement,
218
+ positions: [positioningFunction]
219
+ };
220
+ }
221
+ function getLowerRightCornerPosition(focusedEditableElement, config) {
222
+ return getLowerCornerPosition(focusedEditableElement, config, (rootRect, balloonRect) => {
223
+ return rootRect.left + rootRect.width - balloonRect.width - config.horizontalOffset;
224
+ });
225
+ }
226
+ function getLowerLeftCornerPosition(focusedEditableElement, config) {
227
+ return getLowerCornerPosition(focusedEditableElement, config, rootRect => rootRect.left + config.horizontalOffset);
228
+ }
229
+ function getLowerCornerPosition(focusedEditableElement, config, getBalloonLeft) {
230
+ return (editableElementRect, balloonRect) => {
231
+ const visibleEditableElementRect = editableElementRect.getVisible();
232
+ // Root cropped by ancestors.
233
+ if (!visibleEditableElementRect) {
234
+ return OFF_THE_SCREEN_POSITION;
235
+ }
236
+ if (editableElementRect.width < NARROW_ROOT_WIDTH_THRESHOLD || editableElementRect.height < NARROW_ROOT_HEIGHT_THRESHOLD) {
237
+ return OFF_THE_SCREEN_POSITION;
238
+ }
239
+ let balloonTop;
240
+ if (config.position === 'inside') {
241
+ balloonTop = editableElementRect.bottom - balloonRect.height;
242
+ }
243
+ else {
244
+ balloonTop = editableElementRect.bottom - balloonRect.height / 2;
245
+ }
246
+ balloonTop -= config.verticalOffset;
247
+ const balloonLeft = getBalloonLeft(editableElementRect, balloonRect);
248
+ if (config.position === 'inside') {
249
+ const newBalloonRect = balloonRect.clone().moveTo(balloonLeft, balloonTop);
250
+ // The watermark cannot be positioned in this corner because the corner is not quite visible.
251
+ if (newBalloonRect.getIntersectionArea(visibleEditableElementRect) < newBalloonRect.getArea()) {
252
+ return OFF_THE_SCREEN_POSITION;
253
+ }
254
+ }
255
+ else {
256
+ const firstScrollableEditableElementAncestor = findClosestScrollableAncestor(focusedEditableElement);
257
+ if (firstScrollableEditableElementAncestor) {
258
+ const firstScrollableEditableElementAncestorRect = new Rect(firstScrollableEditableElementAncestor);
259
+ // The watermark cannot be positioned in this corner because the corner is "not visible enough".
260
+ if (visibleEditableElementRect.bottom + balloonRect.height / 2 > firstScrollableEditableElementAncestorRect.bottom) {
261
+ return OFF_THE_SCREEN_POSITION;
262
+ }
263
+ }
264
+ }
265
+ return {
266
+ top: balloonTop,
267
+ left: balloonLeft,
268
+ name: `position_${config.position}-side_${config.side}`,
269
+ config: {
270
+ withArrow: false
271
+ }
272
+ };
273
+ };
274
+ }
275
+ function getNormalizedConfig(editor) {
276
+ const userConfig = editor.config.get('ui.poweredBy');
277
+ const position = userConfig && userConfig.position || 'border';
278
+ return {
279
+ position,
280
+ verticalOffset: position === 'inside' ? 5 : 0,
281
+ horizontalOffset: 5,
282
+ side: editor.locale.contentLanguageDirection === 'ltr' ? 'right' : 'left',
283
+ ...userConfig
284
+ };
285
+ }
package/src/index.d.ts CHANGED
@@ -17,6 +17,8 @@ export { default as SwitchButtonView } from './button/switchbuttonview';
17
17
  export * from './colorgrid/utils';
18
18
  export { default as ColorGridView, type ColorDefinition } from './colorgrid/colorgridview';
19
19
  export { default as ColorTileView } from './colorgrid/colortileview';
20
+ export { default as ColorPickerView } from './colorpicker/colorpickerview';
21
+ export type { ColorPickerConfig, ColorPickerOutputFormat } from './colorpicker/utils';
20
22
  export { default as ComponentFactory } from './componentfactory';
21
23
  export { default as DropdownView } from './dropdown/dropdownview';
22
24
  export { default as DropdownPanelView } from './dropdown/dropdownpanelview';
@@ -53,4 +55,5 @@ export { default as BalloonToolbar, type BalloonToolbarShowEvent } from './toolb
53
55
  export { default as BlockToolbar } from './toolbar/block/blocktoolbar';
54
56
  export { default as View, type UIViewRenderEvent } from './view';
55
57
  export { default as ViewCollection } from './viewcollection';
58
+ export { default as ColorPaletteIcon } from '../theme/icons/color-palette.svg';
56
59
  import './augmentation';
package/src/index.js CHANGED
@@ -16,6 +16,7 @@ export { default as SwitchButtonView } from './button/switchbuttonview';
16
16
  export * from './colorgrid/utils';
17
17
  export { default as ColorGridView } from './colorgrid/colorgridview';
18
18
  export { default as ColorTileView } from './colorgrid/colortileview';
19
+ export { default as ColorPickerView } from './colorpicker/colorpickerview';
19
20
  export { default as ComponentFactory } from './componentfactory';
20
21
  export { default as DropdownView } from './dropdown/dropdownview';
21
22
  export { default as DropdownPanelView } from './dropdown/dropdownpanelview';
@@ -52,4 +53,5 @@ export { default as BalloonToolbar } from './toolbar/balloon/balloontoolbar';
52
53
  export { default as BlockToolbar } from './toolbar/block/blocktoolbar';
53
54
  export { default as View } from './view';
54
55
  export { default as ViewCollection } from './viewcollection';
56
+ export { default as ColorPaletteIcon } from '../theme/icons/color-palette.svg';
55
57
  import './augmentation';
@@ -25,7 +25,8 @@ export default class ListItemView extends View {
25
25
  'ck',
26
26
  'ck-list__item',
27
27
  bind.if('isVisible', 'ck-hidden', value => !value)
28
- ]
28
+ ],
29
+ role: 'presentation'
29
30
  },
30
31
  children: this.children
31
32
  });
@@ -32,6 +32,12 @@ export default class ListView extends View<HTMLUListElement> implements Dropdown
32
32
  * @observable
33
33
  */
34
34
  ariaLabel: string | undefined;
35
+ /**
36
+ * The property reflected by the `role` DOM attribute to be used by assistive technologies.
37
+ *
38
+ * @observable
39
+ */
40
+ role: string | undefined;
35
41
  /**
36
42
  * Helps cycling over focusable {@link #items} in the list.
37
43
  */
@@ -34,6 +34,7 @@ export default class ListView extends View {
34
34
  }
35
35
  });
36
36
  this.set('ariaLabel', undefined);
37
+ this.set('role', undefined);
37
38
  this.setTemplate({
38
39
  tag: 'ul',
39
40
  attributes: {
@@ -42,6 +43,7 @@ export default class ListView extends View {
42
43
  'ck-reset',
43
44
  'ck-list'
44
45
  ],
46
+ role: bind.to('role'),
45
47
  'aria-label': bind.to('ariaLabel')
46
48
  },
47
49
  children: this.items
@@ -7,7 +7,7 @@
7
7
  */
8
8
  /* global window */
9
9
  import { Plugin, icons } from '@ckeditor/ckeditor5-core';
10
- import { Rect, ResizeObserver, getOptimalPosition, env, toUnit } from '@ckeditor/ckeditor5-utils';
10
+ import { Rect, ResizeObserver, getOptimalPosition, toUnit } from '@ckeditor/ckeditor5-utils';
11
11
  import BlockButtonView from './blockbuttonview';
12
12
  import BalloonPanelView from '../../panel/balloon/balloonpanelview';
13
13
  import ToolbarView from '../toolbarview';
@@ -177,12 +177,6 @@ export default class BlockToolbar extends Plugin {
177
177
  isFloating: true
178
178
  });
179
179
  toolbarView.ariaLabel = t('Editor block content toolbar');
180
- // When toolbar lost focus then panel should hide.
181
- toolbarView.focusTracker.on('change:isFocused', (evt, name, is) => {
182
- if (!is) {
183
- this._hidePanel();
184
- }
185
- });
186
180
  return toolbarView;
187
181
  }
188
182
  /**
@@ -209,27 +203,11 @@ export default class BlockToolbar extends Plugin {
209
203
  const editor = this.editor;
210
204
  const t = editor.t;
211
205
  const buttonView = new BlockButtonView(editor.locale);
212
- const bind = buttonView.bindTemplate;
213
206
  buttonView.set({
214
207
  label: t('Edit block'),
215
208
  icon: pilcrow,
216
209
  withText: false
217
210
  });
218
- // Note that this piece over here overrides the default mousedown logic in ButtonView
219
- // to make it work with BlockToolbar. See the implementation of the ButtonView class to learn more.
220
- buttonView.extendTemplate({
221
- on: {
222
- mousedown: bind.to(evt => {
223
- // On Safari we have to force the focus on a button on click as it's the only browser
224
- // that doesn't do that automatically. See #12115.
225
- if (env.isSafari && this.panelView.isVisible) {
226
- this.toolbarView.focus();
227
- }
228
- // Workaround to #12184, see https://github.com/ckeditor/ckeditor5/issues/12184#issuecomment-1199147964.
229
- evt.preventDefault();
230
- })
231
- }
232
- });
233
211
  // Bind the panelView observable properties to the buttonView.
234
212
  buttonView.bind('isOn').to(this.panelView, 'isVisible');
235
213
  buttonView.bind('tooltip').to(this.panelView, 'isVisible', isVisible => !isVisible);
@@ -259,8 +237,8 @@ export default class BlockToolbar extends Plugin {
259
237
  this._hideButton();
260
238
  return;
261
239
  }
262
- // Hides the button when the editor switches to the read-only mode.
263
- if (editor.isReadOnly) {
240
+ // Hides the button when the selection is in non-editable place.
241
+ if (!editor.model.canEditAt(editor.model.document.selection)) {
264
242
  this._hideButton();
265
243
  return;
266
244
  }
@@ -83,7 +83,8 @@ export default class ToolbarView extends View {
83
83
  'aria-label': bind.to('ariaLabel'),
84
84
  style: {
85
85
  maxWidth: bind.to('maxWidth')
86
- }
86
+ },
87
+ tabindex: -1
87
88
  },
88
89
  children: this.children,
89
90
  on: {
@@ -98,6 +99,7 @@ export default class ToolbarView extends View {
98
99
  */
99
100
  render() {
100
101
  super.render();
102
+ this.focusTracker.add(this.element);
101
103
  // Children added before rendering should be known to the #focusTracker.
102
104
  for (const item of this.items) {
103
105
  this.focusTracker.add(item.element);
@@ -0,0 +1,24 @@
1
+ /*
2
+ * 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
+
6
+ .ck.ck-input {
7
+ min-width: unset;
8
+ }
9
+
10
+ .color-picker-hex-input {
11
+ width: max-content;
12
+ }
13
+
14
+ .ck.ck-color-picker__row {
15
+ display: flex;
16
+ flex-direction: row;
17
+ flex-wrap: nowrap;
18
+ justify-content: space-between;
19
+
20
+ & .ck-color-picker__hash-view {
21
+ padding-top: var(--ck-spacing-tiny);
22
+ padding-right: var(--ck-spacing-medium);
23
+ }
24
+ }
@@ -0,0 +1,67 @@
1
+ /*
2
+ * 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
+
6
+ :root {
7
+ --ck-powered-by-line-height: 10px;
8
+ --ck-powered-by-padding-top: 2px;
9
+ --ck-powered-by-padding-left: 4px;
10
+ --ck-powered-by-text-color: hsl(0, 0%, 31%);
11
+ --ck-powered-by-border-radius: var(--ck-border-radius);
12
+ --ck-powered-by-background: hsl(0, 0%, 100%);
13
+ --ck-powered-by-border-color: var(--ck-color-focus-border);
14
+ }
15
+
16
+ .ck.ck-balloon-panel.ck-powered-by-balloon {
17
+ --ck-border-radius: var(--ck-powered-by-border-radius);
18
+
19
+ border: 0;
20
+ box-shadow: none;
21
+ background: var(--ck-powered-by-background);
22
+ min-height: unset;
23
+
24
+ & .ck.ck-powered-by {
25
+ line-height: var(--ck-powered-by-line-height);
26
+
27
+ & a {
28
+ cursor: pointer;
29
+ display: flex;
30
+ align-items: center;
31
+ opacity: .66;
32
+ filter: grayscale(80%);
33
+ line-height: var(--ck-powered-by-line-height);
34
+ padding: var(--ck-powered-by-padding-top) var(--ck-powered-by-padding-left);
35
+ }
36
+
37
+ & .ck-powered-by__label {
38
+ font-size: 7.5px;
39
+ letter-spacing: -.2px;
40
+ padding-left: 2px;
41
+ text-transform: uppercase;
42
+ font-weight: bold;
43
+ margin-right: 4px;
44
+ cursor: pointer;
45
+ line-height: normal;
46
+ color: var(--ck-powered-by-text-color);
47
+
48
+ }
49
+
50
+ & .ck-icon {
51
+ display: block;
52
+ cursor: pointer;
53
+ }
54
+
55
+ &:hover {
56
+ & a {
57
+ filter: grayscale(0%);
58
+ opacity: 1;
59
+ }
60
+ }
61
+ }
62
+
63
+ &[class*="position_border"] {
64
+ border: var(--ck-focus-ring);
65
+ }
66
+ }
67
+
@@ -7,3 +7,4 @@
7
7
  @import "./_reset.css";
8
8
  @import "./_zindex.css";
9
9
  @import "./_transition.css";
10
+ @import "./_poweredby.css";
@@ -0,0 +1 @@
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10.209 18.717A8.5 8.5 0 1 1 18.686 9.6h-.008l.002.12a3 3 0 0 1-2.866 2.997h-.268l-.046-.002v.002h-4.791a2 2 0 1 0 0 4 1 1 0 1 1-.128 1.992 8.665 8.665 0 0 1-.372.008Zm-3.918-7.01a1.25 1.25 0 1 0-2.415-.648 1.25 1.25 0 0 0 2.415.647ZM5.723 8.18a1.25 1.25 0 1 0 .647-2.414 1.25 1.25 0 0 0-.647 2.414ZM9.76 6.155a1.25 1.25 0 1 0 .647-2.415 1.25 1.25 0 0 0-.647 2.415Zm4.028 1.759a1.25 1.25 0 1 0 .647-2.415 1.25 1.25 0 0 0-.647 2.415Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="53" height="10" viewBox="0 0 53 10"><g clip-path="url(#a)"><path fill="#1C2331" d="M31.724 1.492a15.139 15.139 0 0 0 .045 1.16 2.434 2.434 0 0 0-.687-.34 3.68 3.68 0 0 0-1.103-.166 2.332 2.332 0 0 0-1.14.255 1.549 1.549 0 0 0-.686.87c-.15.41-.225.98-.225 1.712 0 .939.148 1.659.444 2.161.297.503.792.754 1.487.754.452.015.9-.094 1.294-.316.296-.174.557-.4.771-.669l.14.852h1.282V.007h-1.623v1.485ZM31 6.496a1.77 1.77 0 0 1-.494.061.964.964 0 0 1-.521-.127.758.758 0 0 1-.296-.466 3.984 3.984 0 0 1-.093-.992 4.208 4.208 0 0 1 .098-1.052.753.753 0 0 1 .307-.477 1.08 1.08 0 0 1 .55-.122c.233-.004.466.026.69.089l.483.144v2.553c-.11.076-.213.143-.307.2a1.73 1.73 0 0 1-.417.189ZM35.68 0l-.702.004c-.322.002-.482.168-.48.497l.004.581c.002.33.164.493.486.49l.702-.004c.322-.002.481-.167.48-.496L36.165.49c-.002-.33-.164-.493-.486-.491ZM36.145 2.313l-1.612.01.034 5.482 1.613-.01-.035-5.482ZM39.623.79 37.989.8 38 2.306l-.946.056.006 1.009.949-.006.024 2.983c.003.476.143.844.419 1.106.275.26.658.39 1.148.387.132 0 .293-.01.483-.03.19-.02.38-.046.57-.08.163-.028.324-.068.482-.119l-.183-1.095-.702.004a.664.664 0 0 1-.456-.123.553.553 0 0 1-.14-.422l-.016-2.621 1.513-.01-.006-1.064-1.514.01-.01-1.503ZM46.226 2.388c-.41-.184-.956-.274-1.636-.27-.673.004-1.215.101-1.627.29-.402.179-.72.505-.888.91-.18.419-.268.979-.264 1.68.004.688.1 1.24.285 1.655.172.404.495.724.9.894.414.18.957.268 1.63.264.68-.004 1.224-.099 1.632-.284.4-.176.714-.501.878-.905.176-.418.263-.971.258-1.658-.004-.702-.097-1.261-.28-1.677a1.696 1.696 0 0 0-.888-.9Zm-.613 3.607a.77.77 0 0 1-.337.501 1.649 1.649 0 0 1-1.317.009.776.776 0 0 1-.343-.497 4.066 4.066 0 0 1-.105-1.02 4.136 4.136 0 0 1 .092-1.03.786.786 0 0 1 .337-.507 1.59 1.59 0 0 1 1.316-.008.79.79 0 0 1 .344.502c.078.337.113.683.105 1.03.012.343-.019.685-.092 1.02ZM52.114 2.07a2.67 2.67 0 0 0-1.128.278c-.39.191-.752.437-1.072.73l-.157-.846-1.273.008.036 5.572 1.623-.01-.024-3.78c.35-.124.646-.22.887-.286.26-.075.53-.114.8-.118l.45-.003.144-1.546-.286.001ZM22.083 7.426l-1.576-2.532a2.137 2.137 0 0 0-.172-.253 1.95 1.95 0 0 0-.304-.29.138.138 0 0 1 .042-.04 1.7 1.7 0 0 0 .328-.374l1.75-2.71c.01-.015.025-.028.024-.048-.01-.01-.021-.007-.031-.007L20.49 1.17a.078.078 0 0 0-.075.045l-.868 1.384c-.23.366-.46.732-.688 1.099a.108.108 0 0 1-.112.06c-.098-.005-.196-.001-.294-.002-.018 0-.038.006-.055-.007.002-.02.002-.039.005-.058a4.6 4.6 0 0 0 .046-.701V1.203c0-.02-.009-.032-.03-.03h-.033L16.93 1.17c-.084 0-.073-.01-.073.076v6.491c-.001.018.006.028.025.027h1.494c.083 0 .072.007.072-.071v-2.19c0-.055-.003-.11-.004-.166a3.366 3.366 0 0 0-.05-.417h.06c.104 0 .209.002.313-.002a.082.082 0 0 1 .084.05c.535.913 1.07 1.824 1.607 2.736a.104.104 0 0 0 .103.062c.554-.003 1.107-.002 1.66-.002l.069-.003-.019-.032-.188-.304ZM27.112 6.555c-.005-.08-.004-.08-.082-.08h-2.414c-.053 0-.106-.003-.159-.011a.279.279 0 0 1-.246-.209.558.558 0 0 1-.022-.15c0-.382 0-.762-.002-1.143 0-.032.007-.049.042-.044h2.504c.029.003.037-.012.034-.038V3.814c0-.089.013-.078-.076-.078h-2.44c-.07 0-.062.003-.062-.06v-.837c0-.047.004-.093.013-.14a.283.283 0 0 1 .241-.246.717.717 0 0 1 .146-.011h2.484c.024.002.035-.009.036-.033l.003-.038.03-.496c.01-.183.024-.365.034-.548.005-.085.003-.087-.082-.094-.218-.018-.437-.038-.655-.05a17.845 17.845 0 0 0-.657-.026 72.994 72.994 0 0 0-1.756-.016 1.7 1.7 0 0 0-.471.064 1.286 1.286 0 0 0-.817.655c-.099.196-.149.413-.145.633v3.875c0 .072.003.144.011.216a1.27 1.27 0 0 0 .711 1.029c.228.113.48.167.734.158.757-.005 1.515.002 2.272-.042.274-.016.548-.034.82-.053.03-.002.043-.008.04-.041-.008-.104-.012-.208-.019-.312a69.964 69.964 0 0 1-.05-.768ZM16.14 7.415l-.127-1.075c-.004-.03-.014-.04-.044-.037a13.125 13.125 0 0 1-.998.073c-.336.01-.672.02-1.008.016-.116-.001-.233-.014-.347-.039a.746.746 0 0 1-.45-.262c-.075-.1-.132-.211-.167-.33a3.324 3.324 0 0 1-.126-.773 9.113 9.113 0 0 1-.015-.749c0-.285.022-.57.065-.852.023-.158.066-.312.127-.46a.728.728 0 0 1 .518-.443 1.64 1.64 0 0 1 .397-.048c.628-.001 1.255.003 1.882.05.022.001.033-.006.036-.026l.003-.031.06-.55c.019-.177.036-.355.057-.532.004-.034-.005-.046-.04-.056a5.595 5.595 0 0 0-1.213-.21 10.783 10.783 0 0 0-.708-.02c-.24-.003-.48.01-.719.041a3.477 3.477 0 0 0-.625.14 1.912 1.912 0 0 0-.807.497c-.185.2-.33.433-.424.688a4.311 4.311 0 0 0-.24 1.096c-.031.286-.045.572-.042.86-.006.43.024.86.091 1.286.04.25.104.497.193.734.098.279.26.53.473.734.214.205.473.358.756.446.344.11.702.17 1.063.177a8.505 8.505 0 0 0 1.578-.083 6.11 6.11 0 0 0 .766-.18c.03-.008.047-.023.037-.057a.157.157 0 0 1-.003-.025Z"/><path fill="#AFE229" d="M6.016 6.69a1.592 1.592 0 0 0-.614.21c-.23.132-.422.32-.56.546-.044.072-.287.539-.287.539l-.836 1.528.009.006c.038.025.08.046.123.063.127.046.26.07.395.073.505.023 1.011-.007 1.517-.003.29.009.58.002.869-.022a.886.886 0 0 0 .395-.116.962.962 0 0 0 .312-.286c.056-.083.114-.163.164-.249.24-.408.48-.816.718-1.226.075-.128.148-.257.222-.386l.112-.192a1.07 1.07 0 0 0 .153-.518l-1.304.023s-1.258-.005-1.388.01Z"/><path fill="#771BFF" d="m2.848 9.044.76-1.39.184-.352c-.124-.067-.245-.14-.367-.21-.346-.204-.706-.384-1.045-.6a.984.984 0 0 1-.244-.207c-.108-.134-.136-.294-.144-.46-.021-.409-.002-.818-.009-1.227-.003-.195 0-.39.003-.585.004-.322.153-.553.427-.713l.833-.488c.22-.13.44-.257.662-.385.05-.029.105-.052.158-.077.272-.128.519-.047.76.085l.044.028c.123.06.242.125.358.196.318.178.635.357.952.537.095.056.187.117.275.184.194.144.254.35.266.578.016.284.007.569.006.853-.001.28.004.558 0 .838.592-.003 1.259 0 1.259 0l.723-.013c-.003-.292-.007-.584-.007-.876 0-.524.015-1.048-.016-1.571-.024-.42-.135-.8-.492-1.067a5.02 5.02 0 0 0-.506-.339A400.52 400.52 0 0 0 5.94.787C5.722.664 5.513.524 5.282.423 5.255.406 5.228.388 5.2.373 4.758.126 4.305-.026 3.807.21c-.097.046-.197.087-.29.14A699.896 699.896 0 0 0 .783 1.948c-.501.294-.773.717-.778 1.31-.004.36-.009.718-.001 1.077.016.754-.017 1.508.024 2.261.016.304.07.6.269.848.127.15.279.28.448.382.622.4 1.283.734 1.92 1.11l.183.109Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h52.4v10H0z"/></clipPath></defs></svg>