Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit 01e29d5

Browse files
authored
Merge pull request #24 from ckeditor/t/23
Feature: The toolbar should support a vertical offset from the top of the web page. Closes #23.
2 parents 68a454c + b434d93 commit 01e29d5

File tree

7 files changed

+338
-98
lines changed

7 files changed

+338
-98
lines changed

src/inlineeditorui.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import ComponentFactory from '@ckeditor/ckeditor5-ui/src/componentfactory';
1111
import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
1212
import enableToolbarKeyboardFocus from '@ckeditor/ckeditor5-ui/src/toolbar/enabletoolbarkeyboardfocus';
13+
import normalizeToolbarConfig from '@ckeditor/ckeditor5-ui/src/toolbar/normalizetoolbarconfig';
1314

1415
/**
1516
* The inline editor UI class.
@@ -44,9 +45,21 @@ export default class InlineEditorUI {
4445
*/
4546
this.focusTracker = new FocusTracker();
4647

48+
/**
49+
* A normalized `config.toolbar` object.
50+
*
51+
* @type {Object}
52+
* @private
53+
*/
54+
this._toolbarConfig = normalizeToolbarConfig( editor.config.get( 'toolbar' ) );
55+
4756
// Set–up the view#panel.
4857
view.panel.bind( 'isVisible' ).to( this.focusTracker, 'isFocused' );
4958

59+
if ( this._toolbarConfig && this._toolbarConfig.viewportTopOffset ) {
60+
view.viewportTopOffset = this._toolbarConfig.viewportTopOffset;
61+
}
62+
5063
// https://github.com/ckeditor/ckeditor5-editor-inline/issues/4
5164
view.listenTo( editor.editing.view, 'render', () => {
5265
// Don't pin if the panel is not already visible. It prevents the panel
@@ -78,7 +91,10 @@ export default class InlineEditorUI {
7891
const editor = this.editor;
7992

8093
this.view.init();
81-
this.view.toolbar.fillFromConfig( editor.config.get( 'toolbar' ), this.componentFactory );
94+
95+
if ( this._toolbarConfig ) {
96+
this.view.toolbar.fillFromConfig( this._toolbarConfig.items, this.componentFactory );
97+
}
8298

8399
enableToolbarKeyboardFocus( {
84100
origin: editor.editing.view,

src/inlineeditoruiview.js

Lines changed: 103 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,6 @@ import BalloonPanelView from '@ckeditor/ckeditor5-ui/src/panel/balloon/balloonpa
1313
import ToolbarView from '@ckeditor/ckeditor5-ui/src/toolbar/toolbarview';
1414
import Template from '@ckeditor/ckeditor5-ui/src/template';
1515

16-
// A set of positioning functions used by the
17-
// {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#panel}.
18-
//
19-
// @private
20-
// @type {module:utils/dom/position~Options#positions}
21-
const panelPositions = [
22-
( editableRect, panelRect ) => {
23-
return {
24-
top: getPanelPositionTop( editableRect, panelRect ),
25-
left: editableRect.left,
26-
name: 'toolbar_west'
27-
};
28-
},
29-
( editableRect, panelRect ) => {
30-
return {
31-
top: getPanelPositionTop( editableRect, panelRect ),
32-
left: editableRect.left + editableRect.width - panelRect.width,
33-
name: 'toolbar_east'
34-
};
35-
}
36-
];
37-
3816
/**
3917
* Inline editor UI view. Uses inline editable and floating toolbar.
4018
*
@@ -57,6 +35,21 @@ export default class InlineEditorUIView extends EditorUIView {
5735
*/
5836
this.toolbar = new ToolbarView( locale );
5937

38+
/**
39+
* The offset from the top edge of the web browser's viewport which makes the
40+
* UI become sticky. The default value is `0`, which means the UI becomes
41+
* sticky when it's upper edge touches the top of the page viewport.
42+
*
43+
* This attribute is useful when the web page has UI elements positioned to the top
44+
* either using `position: fixed` or `position: sticky`, which would cover the
45+
* UI or vice–versa (depending on the `z-index` hierarchy).
46+
*
47+
* @readonly
48+
* @observable
49+
* @member {Number} #viewportTopOffset
50+
*/
51+
this.set( 'viewportTopOffset', 0 );
52+
6053
Template.extend( this.toolbar.template, {
6154
attributes: {
6255
class: [
@@ -78,6 +71,53 @@ export default class InlineEditorUIView extends EditorUIView {
7871

7972
this.panel.withArrow = false;
8073

74+
/**
75+
* A set of positioning functions used by the {@link #panel} to float around
76+
* {@link #editableElement}.
77+
*
78+
* The positioning functions are as follows:
79+
*
80+
* * West:
81+
*
82+
* [ Panel ]
83+
* +------------------+
84+
* | #editableElement |
85+
* +------------------+
86+
*
87+
* +------------------+
88+
* | #editableElement |
89+
* |[ Panel ] |
90+
* | |
91+
* +------------------+
92+
*
93+
* +------------------+
94+
* | #editableElement |
95+
* +------------------+
96+
* [ Panel ]
97+
*
98+
* * East:
99+
*
100+
* [ Panel ]
101+
* +------------------+
102+
* | #editableElement |
103+
* +------------------+
104+
*
105+
* +------------------+
106+
* | #editableElement |
107+
* | [ Panel ]|
108+
* | |
109+
* +------------------+
110+
*
111+
* +------------------+
112+
* | #editableElement |
113+
* +------------------+
114+
* [ Panel ]
115+
*
116+
* @readonly
117+
* @type {module:utils/dom/position~Options#positions}
118+
*/
119+
this.panelPositions = this._getPanelPositions();
120+
81121
Template.extend( this.panel.template, {
82122
attributes: {
83123
class: 'ck-toolbar-container'
@@ -113,73 +153,50 @@ export default class InlineEditorUIView extends EditorUIView {
113153
}
114154

115155
/**
116-
* A set of positioning functions used by the {@link #panel} to float around
117-
* {@link #editableElement}.
118-
*
119-
* The positioning functions are as follows:
120-
*
121-
* * West:
122-
*
123-
* [ Panel ]
124-
* +------------------+
125-
* | #editableElement |
126-
* +------------------+
127-
*
128-
* +------------------+
129-
* | #editableElement |
130-
* |[ Panel ] |
131-
* | |
132-
* +------------------+
133-
*
134-
* +------------------+
135-
* | #editableElement |
136-
* +------------------+
137-
* [ Panel ]
138-
*
139-
* * East:
156+
* Determines panel top position of the {@link #panel} in {@link #panelPositions}.
140157
*
141-
* [ Panel ]
142-
* +------------------+
143-
* | #editableElement |
144-
* +------------------+
145-
*
146-
* +------------------+
147-
* | #editableElement |
148-
* | [ Panel ]|
149-
* | |
150-
* +------------------+
151-
*
152-
* +------------------+
153-
* | #editableElement |
154-
* +------------------+
155-
* [ Panel ]
156-
*
157-
* @readonly
158-
* @type {module:utils/dom/position~Options#positions}
158+
* @private
159+
* @param {module:utils/dom/rect~Rect} editableRect Rect of the
160+
* {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#editableElement}.
161+
* @param {module:utils/dom/rect~Rect} panelRect Rect of the
162+
* {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#panel}.
159163
*/
160-
get panelPositions() {
161-
return panelPositions;
164+
_getPanelPositionTop( editableRect, panelRect ) {
165+
let top;
166+
167+
if ( editableRect.top > panelRect.height + this.viewportTopOffset ) {
168+
top = editableRect.top - panelRect.height;
169+
} else if ( editableRect.bottom > panelRect.height + this.viewportTopOffset + 50 ) {
170+
top = this.viewportTopOffset;
171+
} else {
172+
top = editableRect.bottom;
173+
}
174+
175+
return top;
162176
}
163-
}
164177

165-
// Determines panel top position for
166-
// {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#panelPositions}
167-
//
168-
// @private
169-
// @param {module:utils/dom/rect~Rect} editableRect Rect of the
170-
// {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#editableElement}.
171-
// @param {module:utils/dom/rect~Rect} panelRect Rect of the
172-
// {@link module:editor-inline/inlineeditoruiview~InlineEditableUIView#panel}.
173-
function getPanelPositionTop( editableRect, panelRect ) {
174-
let top;
175-
176-
if ( editableRect.top > panelRect.height ) {
177-
top = editableRect.top - panelRect.height;
178-
} else if ( editableRect.bottom > panelRect.height + 50 ) {
179-
top = 0;
180-
} else {
181-
top = editableRect.bottom;
178+
/**
179+
* Returns the positions for {@link #panelPositions}.
180+
*
181+
* @private
182+
* @returns module:utils/dom/position~Options#positions
183+
*/
184+
_getPanelPositions() {
185+
return [
186+
( editableRect, panelRect ) => {
187+
return {
188+
top: this._getPanelPositionTop( editableRect, panelRect ),
189+
left: editableRect.left,
190+
name: 'toolbar_west'
191+
};
192+
},
193+
( editableRect, panelRect ) => {
194+
return {
195+
top: this._getPanelPositionTop( editableRect, panelRect ),
196+
left: editableRect.left + editableRect.width - panelRect.width,
197+
name: 'toolbar_east'
198+
};
199+
}
200+
];
182201
}
183-
184-
return top;
185202
}

tests/inlineeditorui.js

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,24 @@ describe( 'InlineEditorUI', () => {
2727
editorElement = document.createElement( 'div' );
2828
document.body.appendChild( editorElement );
2929

30-
editor = new ClassicTestEditor( editorElement, {
30+
return ClassicTestEditor.create( editorElement, {
3131
toolbar: [ 'foo', 'bar' ]
32-
} );
32+
} )
33+
.then( newEditor => {
34+
editor = newEditor;
3335

34-
view = new InlineEditorUIView( editor.locale );
35-
ui = new InlineEditorUI( editor, view );
36-
editable = editor.editing.view.getRoot();
36+
view = new InlineEditorUIView( editor.locale );
37+
ui = new InlineEditorUI( editor, view );
38+
editable = editor.editing.view.getRoot();
3739

38-
ui.componentFactory.add( 'foo', viewCreator( 'foo' ) );
39-
ui.componentFactory.add( 'bar', viewCreator( 'bar' ) );
40+
ui.componentFactory.add( 'foo', viewCreator( 'foo' ) );
41+
ui.componentFactory.add( 'bar', viewCreator( 'bar' ) );
42+
} );
43+
} );
44+
45+
afterEach( () => {
46+
editorElement.remove();
47+
editor.destroy();
4048
} );
4149

4250
describe( 'constructor()', () => {
@@ -65,6 +73,35 @@ describe( 'InlineEditorUI', () => {
6573
expect( view.panel.isVisible ).to.be.true;
6674
} );
6775

76+
it( 'doesn\'t set the view#viewportTopOffset, if not specified in the config', () => {
77+
expect( view.viewportTopOffset ).to.equal( 0 );
78+
} );
79+
80+
it( 'sets view#viewportTopOffset, if specified', () => {
81+
editorElement = document.createElement( 'div' );
82+
document.body.appendChild( editorElement );
83+
84+
return ClassicTestEditor.create( editorElement, {
85+
toolbar: {
86+
items: [ 'foo', 'bar' ],
87+
viewportTopOffset: 100
88+
}
89+
} )
90+
.then( editor => {
91+
view = new InlineEditorUIView( editor.locale );
92+
ui = new InlineEditorUI( editor, view );
93+
editable = editor.editing.view.getRoot();
94+
95+
ui.componentFactory.add( 'foo', viewCreator( 'foo' ) );
96+
ui.componentFactory.add( 'bar', viewCreator( 'bar' ) );
97+
98+
expect( view.viewportTopOffset ).to.equal( 100 );
99+
100+
editorElement.remove();
101+
return editor.destroy();
102+
} );
103+
} );
104+
68105
// https://github.com/ckeditor/ckeditor5-editor-inline/issues/4
69106
it( 'pin() is called on editor.editable.view#render', () => {
70107
const spy = sinon.spy( view.panel, 'pin' );
@@ -133,11 +170,40 @@ describe( 'InlineEditorUI', () => {
133170
sinon.assert.calledOnce( spy );
134171
} );
135172

136-
it( 'fills view.toolbar#items with editor config', () => {
137-
const spy = testUtils.sinon.spy( view.toolbar, 'fillFromConfig' );
173+
describe( 'view.toolbar#items', () => {
174+
it( 'are filled with the config.toolbar (specified as an Array)', () => {
175+
const spy = testUtils.sinon.spy( view.toolbar, 'fillFromConfig' );
138176

139-
ui.init();
140-
sinon.assert.calledWithExactly( spy, editor.config.get( 'toolbar' ), ui.componentFactory );
177+
ui.init();
178+
sinon.assert.calledWithExactly( spy, editor.config.get( 'toolbar' ), ui.componentFactory );
179+
} );
180+
181+
it( 'are filled with the config.toolbar (specified as an Object)', () => {
182+
editorElement = document.createElement( 'div' );
183+
document.body.appendChild( editorElement );
184+
185+
return ClassicTestEditor.create( editorElement, {
186+
toolbar: {
187+
items: [ 'foo', 'bar' ],
188+
viewportTopOffset: 100
189+
}
190+
} )
191+
.then( editor => {
192+
view = new InlineEditorUIView( editor.locale );
193+
ui = new InlineEditorUI( editor, view );
194+
195+
ui.componentFactory.add( 'foo', viewCreator( 'foo' ) );
196+
ui.componentFactory.add( 'bar', viewCreator( 'bar' ) );
197+
198+
const spy = testUtils.sinon.spy( view.toolbar, 'fillFromConfig' );
199+
200+
ui.init();
201+
sinon.assert.calledWithExactly( spy,
202+
editor.config.get( 'toolbar.items' ),
203+
ui.componentFactory
204+
);
205+
} );
206+
} );
141207
} );
142208

143209
it( 'initializes keyboard navigation between view#toolbar and view#editable', () => {

0 commit comments

Comments
 (0)