From 20976d9f3b8eb73f7ef583b2c674a3cb00a8a2b4 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 6 Mar 2019 16:59:04 +0100 Subject: [PATCH 1/3] Docs: Improved focus management across roots in a multiple roots example (guide). --- docs/_snippets/examples/multi-root-editor.js | 49 ++++++++++++++++++- .../framework/guides/custom-editor-creator.md | 49 ++++++++++++++++++- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/docs/_snippets/examples/multi-root-editor.js b/docs/_snippets/examples/multi-root-editor.js index d9a045c90ef..cbb1e412500 100644 --- a/docs/_snippets/examples/multi-root-editor.js +++ b/docs/_snippets/examples/multi-root-editor.js @@ -177,8 +177,33 @@ class MultirootEditorUI extends EditorUI { const editor = this.editor; const editingView = editor.editing.view; + let lastFocusedEditableElement; + view.render(); + // Keep track of the last focused editable element. Knowing which one was focused + // is useful when the focus moves from editable to other UI components like balloons + // (especially inputs) but the editable remains the "focus context" (e.g. link balloon + // attached to a link in an editable). In this case, the editable should preserve visual + // focus styles. + this.focusTracker.on( 'change:focusedElement', ( evt, name, focusedElement ) => { + for ( const editable of this.view.editables ) { + if ( editable.element === focusedElement ) { + lastFocusedEditableElement = editable.element; + } + } + } ); + + // If the focus tracker loses focus, stop tracking the last focused editable element. + // Wherever the focus is restored, it will no longer be in the context of that editable + // because the focus "came from the outside", as opposed to the focus moving from one element + // to another withing the editor UI. + this.focusTracker.on( 'change:isFocused', ( evt, name, isFocused ) => { + if ( !isFocused ) { + lastFocusedEditableElement = null; + } + } ); + for ( const editable of this.view.editables ) { // The editable UI element in DOM is available for sure only after the editor UI view has been rendered. // But it can be available earlier if a DOM element has been passed to DecoupledEditor.create(). @@ -200,7 +225,29 @@ class MultirootEditorUI extends EditorUI { // it isn't), e.g. by setting the proper CSS class, visually announcing focus to the user. // Doing otherwise will result in editable focus styles disappearing, once e.g. the // toolbar gets focused. - editable.bind( 'isFocused' ).to( this.focusTracker ); + editable.bind( 'isFocused' ).to( this.focusTracker, 'isFocused', this.focusTracker, 'focusedElement', + ( isFocused, focusedElement ) => { + // When the focus tracker is blurred, it means the focus moved out of the editor UI. + // No editable will maintain focus then. + if ( !isFocused ) { + return false; + } + + // If the focus tracker says the editor UI is focused and currently focused element + // is the editable, then the editable should be visually marked as focused too. + if ( focusedElement === editableElement ) { + return true; + } + // If the focus tracker says the editor UI is focused but the focused element is + // not an editable, it is possible that the editable is still (context–)focused. + // For instance, the focused element could be an input inside of a balloon attached + // to the content in the editable. In such case, the editable should remain _visually_ + // focused even though technically the focus is somewhere else. The focus moved from + // the editable to the input but the focus context remained the same. + else { + return lastFocusedEditableElement === editableElement; + } + } ); // Bind the editable UI element to the editing view, making it an end– and entry–point // of the editor's engine. This is where the engine meets the UI. diff --git a/docs/framework/guides/custom-editor-creator.md b/docs/framework/guides/custom-editor-creator.md index 85eb26c92e4..f7e5a7e6f36 100644 --- a/docs/framework/guides/custom-editor-creator.md +++ b/docs/framework/guides/custom-editor-creator.md @@ -169,8 +169,33 @@ class MultirootEditorUI extends EditorUI { const editor = this.editor; const editingView = editor.editing.view; + let lastFocusedEditableElement; + view.render(); + // Keep track of the last focused editable element. Knowing which one was focused + // is useful when the focus moves from editable to other UI components like balloons + // (especially inputs) but the editable remains the "focus context" (e.g. link balloon + // attached to a link in an editable). In this case, the editable should preserve visual + // focus styles. + this.focusTracker.on( 'change:focusedElement', ( evt, name, focusedElement ) => { + for ( const editable of this.view.editables ) { + if ( editable.element === focusedElement ) { + lastFocusedEditableElement = editable.element; + } + } + } ); + + // If the focus tracker loses focus, stop tracking the last focused editable element. + // Wherever the focus is restored, it will no longer be in the context of that editable + // because the focus "came from the outside", as opposed to the focus moving from one element + // to another withing the editor UI. + this.focusTracker.on( 'change:isFocused', ( evt, name, isFocused ) => { + if ( !isFocused ) { + lastFocusedEditableElement = null; + } + } ); + for ( const editable of this.view.editables ) { // The editable UI element in DOM is available for sure only after the editor UI view has been rendered. // But it can be available earlier if a DOM element has been passed to DecoupledEditor.create(). @@ -192,7 +217,29 @@ class MultirootEditorUI extends EditorUI { // it isn't), e.g. by setting the proper CSS class, visually announcing focus to the user. // Doing otherwise will result in editable focus styles disappearing, once e.g. the // toolbar gets focused. - editable.bind( 'isFocused' ).to( this.focusTracker ); + editable.bind( 'isFocused' ).to( this.focusTracker, 'isFocused', this.focusTracker, 'focusedElement', + ( isFocused, focusedElement ) => { + // When the focus tracker is blurred, it means the focus moved out of the editor UI. + // No editable will maintain focus then. + if ( !isFocused ) { + return false; + } + + // If the focus tracker says the editor UI is focused and currently focused element + // is the editable, then the editable should be visually marked as focused too. + if ( focusedElement === editableElement ) { + return true; + } + // If the focus tracker says the editor UI is focused but the focused element is + // not an editable, it is possible that the editable is still (context–)focused. + // For instance, the focused element could be an input inside of a balloon attached + // to the content in the editable. In such case, the editable should remain _visually_ + // focused even though technically the focus is somewhere else. The focus moved from + // the editable to the input but the focus context remained the same. + else { + return lastFocusedEditableElement === editableElement; + } + } ); // Bind the editable UI element to the editing view, making it an end– and entry–point // of the editor's engine. This is where the engine meets the UI. From 276bb95560e6bed1ca707154157e685c60d5df64 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 6 Mar 2019 17:22:37 +0100 Subject: [PATCH 2/3] Docs: Allowed different placeholder text values across editor roots in the multiple roots example (and guide). --- docs/_snippets/examples/multi-root-editor.js | 9 +++++++-- docs/framework/guides/custom-editor-creator.md | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/_snippets/examples/multi-root-editor.js b/docs/_snippets/examples/multi-root-editor.js index cbb1e412500..827aa6c36e2 100644 --- a/docs/_snippets/examples/multi-root-editor.js +++ b/docs/_snippets/examples/multi-root-editor.js @@ -308,7 +308,7 @@ class MultirootEditorUI extends EditorUI { const editingRoot = editingView.document.getRoot( editable.name ); const sourceElement = this.getEditableElement( editable.name ); - const placeholderText = editor.config.get( 'placeholder' ) || + const placeholderText = editor.config.get( 'placeholder' )[ editable.name ] || sourceElement && sourceElement.tagName.toLowerCase() === 'textarea' && sourceElement.getAttribute( 'placeholder' ); if ( placeholderText ) { @@ -411,7 +411,12 @@ MultirootEditor 'mergeTableCells' ] }, - placeholder: 'Type your text here...', + placeholder: { + header: 'Header text goes here', + content: 'Type content here', + footerleft: 'Left footer content', + footerright: 'Right footer content' + }, cloudServices: CS_CONFIG } ) .then( newEditor => { diff --git a/docs/framework/guides/custom-editor-creator.md b/docs/framework/guides/custom-editor-creator.md index f7e5a7e6f36..57d325444b0 100644 --- a/docs/framework/guides/custom-editor-creator.md +++ b/docs/framework/guides/custom-editor-creator.md @@ -300,7 +300,7 @@ class MultirootEditorUI extends EditorUI { const editingRoot = editingView.document.getRoot( editable.name ); const sourceElement = this.getEditableElement( editable.name ); - const placeholderText = editor.config.get( 'placeholder' ) || + const placeholderText = editor.config.get( 'placeholder' )[ editable.name ] || sourceElement && sourceElement.tagName.toLowerCase() === 'textarea' && sourceElement.getAttribute( 'placeholder' ); if ( placeholderText ) { @@ -461,7 +461,12 @@ MultirootEditor 'mergeTableCells' ] }, - placeholder: 'Type your text here...' + placeholder: { + header: 'Header text goes here', + content: 'Type content here', + footerleft: 'Left footer content', + footerright: 'Right footer content' + }, } ) .then( newEditor => { document.querySelector( '#toolbar' ).appendChild( newEditor.ui.view.toolbar.element ); From 46ae06be884f43143c0f03ba54b988ceaf84b799 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Thu, 7 Mar 2019 10:45:46 +0100 Subject: [PATCH 3/3] =?UTF-8?q?Docs:=20Added=20some=20space=20around=20the?= =?UTF-8?q?=20multi=E2=80=93root=20editor=20toolbar.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/_snippets/examples/multi-root-editor.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/_snippets/examples/multi-root-editor.html b/docs/_snippets/examples/multi-root-editor.html index 62176a49fdc..55f8fe0f9df 100644 --- a/docs/_snippets/examples/multi-root-editor.html +++ b/docs/_snippets/examples/multi-root-editor.html @@ -35,3 +35,10 @@

Walking the capitals of Europe: Warsaw

+ +