@@ -14,7 +14,6 @@ import BlockButtonView from './blockbuttonview';
1414import BalloonPanelView from '../../panel/balloon/balloonpanelview' ;
1515import ToolbarView from '../toolbarview' ;
1616
17- import ClickObserver from '@ckeditor/ckeditor5-engine/src/view/observer/clickobserver' ;
1817import clickOutsideHandler from '../../bindings/clickoutsidehandler' ;
1918
2019import { getOptimalPosition } from '@ckeditor/ckeditor5-utils/src/dom/position' ;
@@ -66,10 +65,8 @@ export default class BlockToolbar extends Plugin {
6665 /**
6766 * @inheritDoc
6867 */
69- init ( ) {
70- const editor = this . editor ;
71-
72- editor . editing . view . addObserver ( ClickObserver ) ;
68+ constructor ( editor ) {
69+ super ( editor ) ;
7370
7471 /**
7572 * The toolbar view.
@@ -99,18 +96,39 @@ export default class BlockToolbar extends Plugin {
9996 activator : ( ) => this . panelView . isVisible ,
10097 callback : ( ) => this . _hidePanel ( )
10198 } ) ;
99+ }
102100
103- // Try to hide button when the editor switches to the read-only mode.
104- // Do not hide when panel if already visible to avoid a confusing UX when the panel
105- // unexpectedly disappears.
106- this . listenTo ( editor , 'change:isReadOnly' , ( ) => {
107- if ( ! this . panelView . isVisible ) {
108- this . buttonView . isVisible = false ;
101+ /**
102+ * @inheritDoc
103+ */
104+ init ( ) {
105+ const editor = this . editor ;
106+
107+ // Hides panel on a direct selection change.
108+ this . listenTo ( editor . model . document . selection , 'change:range' , ( evt , data ) => {
109+ if ( data . directChange ) {
110+ this . _hidePanel ( ) ;
109111 }
110112 } ) ;
111113
112- // Enable as default.
113- this . _initListeners ( ) ;
114+ this . listenTo ( editor . ui , 'update' , ( ) => this . _updateButton ( ) ) ;
115+ // `low` priority is used because of https://github.com/ckeditor/ckeditor5-core/issues/133.
116+ this . listenTo ( editor , 'change:isReadOnly' , ( ) => this . _updateButton ( ) , { priority : 'low' } ) ;
117+ this . listenTo ( editor . ui . focusTracker , 'change:isFocused' , ( ) => this . _updateButton ( ) ) ;
118+
119+ // Reposition button on resize.
120+ this . listenTo ( this . buttonView , 'change:isVisible' , ( evt , name , isVisible ) => {
121+ if ( isVisible ) {
122+ // Keep correct position of button and panel on window#resize.
123+ this . buttonView . listenTo ( window , 'resize' , ( ) => this . _updateButton ( ) ) ;
124+ } else {
125+ // Stop repositioning button when is hidden.
126+ this . buttonView . stopListening ( window , 'resize' ) ;
127+
128+ // Hide the panel when the button disappears.
129+ this . _hidePanel ( ) ;
130+ }
131+ } ) ;
114132 }
115133
116134 /**
@@ -143,10 +161,15 @@ export default class BlockToolbar extends Plugin {
143161
144162 toolbarView . extendTemplate ( {
145163 attributes : {
146- class : [
147- // https://github.com/ckeditor/ckeditor5-editor-inline/issues/11
148- 'ck-toolbar_floating'
149- ]
164+ // https://github.com/ckeditor/ckeditor5-editor-inline/issues/11
165+ class : [ 'ck-toolbar_floating' ]
166+ }
167+ } ) ;
168+
169+ // When toolbar lost focus then panel should hide.
170+ toolbarView . focusTracker . on ( 'change:isFocused' , ( evt , name , is ) => {
171+ if ( ! is ) {
172+ this . _hidePanel ( ) ;
150173 }
151174 } ) ;
152175
@@ -213,93 +236,62 @@ export default class BlockToolbar extends Plugin {
213236 }
214237
215238 /**
216- * Starts displaying the button next to allowed elements.
239+ * Shows or hides the button.
240+ * When the all conditions for displaying button are matched then shows the button. Hides otherwise.
217241 *
218242 * @private
219243 */
220- _initListeners ( ) {
244+ _updateButton ( ) {
221245 const editor = this . editor ;
222246 const model = editor . model ;
223247 const view = editor . editing . view ;
224- let modelTarget , domTarget ;
225248
226- // Hides panel on a direct selection change.
227- this . listenTo ( editor . model . document . selection , 'change:range' , ( evt , data ) => {
228- if ( data . directChange ) {
229- this . _hidePanel ( ) ;
230- }
231- } ) ;
249+ // Hides the button when the editor is not focused.
250+ if ( ! editor . ui . focusTracker . isFocused ) {
251+ this . _hideButton ( ) ;
232252
233- this . listenTo ( editor . ui , 'update' , ( ) => {
234- // Get first selected block, button will be attached to this element.
235- modelTarget = Array . from ( model . document . selection . getSelectedBlocks ( ) ) [ 0 ] ;
253+ return ;
254+ }
236255
237- // Do not attach block button when there is no enabled item in toolbar for current block element .
238- if ( ! modelTarget || Array . from ( this . toolbarView . items ) . every ( item => ! item . isEnabled ) ) {
239- this . buttonView . isVisible = false ;
256+ // Hides the button when the editor switches to the read-only mode .
257+ if ( editor . isReadOnly ) {
258+ this . _hideButton ( ) ;
240259
241- return ;
242- }
260+ return ;
261+ }
243262
244- // Get DOM target element.
245- domTarget = view . domConverter . mapViewToDom ( editor . editing . mapper . toViewElement ( modelTarget ) ) ;
263+ // Get the first selected block, button will be attached to this element.
264+ const modelTarget = Array . from ( model . document . selection . getSelectedBlocks ( ) ) [ 0 ] ;
246265
247- // Show block button.
248- this . buttonView . isVisible = true ;
266+ // Hides the button when there is no enabled item in toolbar for the current block element.
267+ if ( ! modelTarget || Array . from ( this . toolbarView . items ) . every ( item => ! item . isEnabled ) ) {
268+ this . _hideButton ( ) ;
249269
250- // Attach block button to target DOM element.
251- this . _attachButtonToElement ( domTarget ) ;
270+ return ;
271+ }
252272
253- // When panel is opened then refresh it position to be properly aligned with block button.
254- if ( this . panelView . isVisible ) {
255- this . _showPanel ( ) ;
256- }
257- } , { priority : 'low' } ) ;
273+ // Get DOM target element.
274+ const domTarget = view . domConverter . mapViewToDom ( editor . editing . mapper . toViewElement ( modelTarget ) ) ;
258275
259- this . listenTo ( this . buttonView , 'change:isVisible' , ( evt , name , isVisible ) => {
260- if ( isVisible ) {
261- // Keep correct position of button and panel on window#resize.
262- this . buttonView . listenTo ( window , 'resize' , ( ) => this . _attachButtonToElement ( domTarget ) ) ;
263- } else {
264- // Stop repositioning button when is hidden.
265- this . buttonView . stopListening ( window , 'resize' ) ;
276+ // Show block button.
277+ this . buttonView . isVisible = true ;
266278
267- // Hide the panel when the button disappears.
268- this . _hidePanel ( ) ;
269- }
270- } ) ;
279+ // Attach block button to target DOM element.
280+ this . _attachButtonToElement ( domTarget ) ;
281+
282+ // When panel is opened then refresh it position to be properly aligned with block button.
283+ if ( this . panelView . isVisible ) {
284+ this . _showPanel ( ) ;
285+ }
271286 }
272287
273288 /**
274- * Attaches the { @link #buttonView} to the target block of content .
289+ * Hides the button .
275290 *
276- * @protected
277- * @param {HTMLElement } targetElement Target element.
291+ * @private
278292 */
279- _attachButtonToElement ( targetElement ) {
280- const contentStyles = window . getComputedStyle ( targetElement ) ;
281-
282- const editableRect = new Rect ( this . editor . ui . view . editableElement ) ;
283- const contentPaddingTop = parseInt ( contentStyles . paddingTop , 10 ) ;
284- // When line height is not an integer then thread it as "normal".
285- // MDN says that 'normal' == ~1.2 on desktop browsers.
286- const contentLineHeight = parseInt ( contentStyles . lineHeight , 10 ) || parseInt ( contentStyles . fontSize , 10 ) * 1.2 ;
287-
288- const position = getOptimalPosition ( {
289- element : this . buttonView . element ,
290- target : targetElement ,
291- positions : [
292- ( contentRect , buttonRect ) => {
293- return {
294- top : contentRect . top + contentPaddingTop + ( ( contentLineHeight - buttonRect . height ) / 2 ) ,
295- left : editableRect . left - buttonRect . width
296- } ;
297- }
298- ]
299- } ) ;
300-
301- this . buttonView . top = position . top ;
302- this . buttonView . left = position . left ;
293+ _hideButton ( ) {
294+ this . buttonView . isVisible = false ;
303295 }
304296
305297 /**
@@ -334,6 +326,38 @@ export default class BlockToolbar extends Plugin {
334326 this . editor . editing . view . focus ( ) ;
335327 }
336328 }
329+
330+ /**
331+ * Attaches the {@link #buttonView} to the target block of content.
332+ *
333+ * @protected
334+ * @param {HTMLElement } targetElement Target element.
335+ */
336+ _attachButtonToElement ( targetElement ) {
337+ const contentStyles = window . getComputedStyle ( targetElement ) ;
338+
339+ const editableRect = new Rect ( this . editor . ui . view . editableElement ) ;
340+ const contentPaddingTop = parseInt ( contentStyles . paddingTop , 10 ) ;
341+ // When line height is not an integer then thread it as "normal".
342+ // MDN says that 'normal' == ~1.2 on desktop browsers.
343+ const contentLineHeight = parseInt ( contentStyles . lineHeight , 10 ) || parseInt ( contentStyles . fontSize , 10 ) * 1.2 ;
344+
345+ const position = getOptimalPosition ( {
346+ element : this . buttonView . element ,
347+ target : targetElement ,
348+ positions : [
349+ ( contentRect , buttonRect ) => {
350+ return {
351+ top : contentRect . top + contentPaddingTop + ( ( contentLineHeight - buttonRect . height ) / 2 ) ,
352+ left : editableRect . left - buttonRect . width
353+ } ;
354+ }
355+ ]
356+ } ) ;
357+
358+ this . buttonView . top = position . top ;
359+ this . buttonView . left = position . left ;
360+ }
337361}
338362
339363/**
0 commit comments