-
Notifications
You must be signed in to change notification settings - Fork 2
T/10: Provide necessary UI components for Classic Creator #11
Changes from 11 commits
9adfde0
d10aeda
92c2ba2
0a6eef1
136862c
0022b30
2a5b90e
274b55d
d11c824
7ea1ad1
421639a
6d9fce5
b445b33
20e5a39
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/** | ||
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
import View from '../view.js'; | ||
|
||
/** | ||
* The basic iframe view class. | ||
* | ||
* @memberOf ui.iframe | ||
* @extends ui.View | ||
*/ | ||
export default class IframeView extends View { | ||
/** | ||
* Creates a new instance of the IframeView. | ||
* | ||
* @param {ui.iframe.IframeModel} [model] (View)Model of this IframeView. | ||
* @param {utils.Locale} [locale] The {@link ckeditor5.Editor#locale editor's locale} instance. | ||
*/ | ||
constructor( model, locale ) { | ||
super( model, locale ); | ||
|
||
this.template = { | ||
tag: 'iframe', | ||
attributes: { | ||
class: [ 'ck-reset-all' ], | ||
// It seems that we need to allow scripts in order to be able to listen to events. | ||
// TODO: Research that. Perhaps the src must be set? | ||
sandbox: 'allow-same-origin allow-scripts' | ||
}, | ||
on: { | ||
load: 'loaded' | ||
} | ||
}; | ||
|
||
/** | ||
* A promise returned by {@link init} since iframe loading may be asynchronous. | ||
* | ||
* **Note**: Listening to `load` in {@link init} makes no sense because at this point | ||
* the element is already in the DOM and the `load` event might already be fired. | ||
* | ||
* See {@link _iframeDeferred}. | ||
* | ||
* @private | ||
* @member {Object} ui.iframe.IframeView | ||
*/ | ||
this._iframePromise = new Promise( ( resolve, reject ) => { | ||
/** | ||
* A deferred object used to resolve the iframe promise associated with | ||
* asynchronous loading of `contentDocument`. See {@link _iframePromise}. | ||
* | ||
* @private | ||
* @member {Object} ui.iframe.IframeView | ||
*/ | ||
this._iframeDeferred = { resolve, reject }; | ||
} ); | ||
|
||
this.on( 'loaded', () => { | ||
this._iframeDeferred.resolve(); | ||
} ); | ||
} | ||
|
||
/** | ||
* Initializes iframe {@link element} and returns a `Promise` for asynchronous | ||
* child `contentDocument` loading process. See {@link _iframePromise}. | ||
* | ||
* @returns {Promise} A promise which resolves once the iframe `contentDocument` has | ||
* been {@link ui.iframe.IframeView#loaded loaded}. | ||
*/ | ||
init() { | ||
super.init(); | ||
|
||
return this._iframePromise; | ||
} | ||
} | ||
|
||
/** | ||
* Fired when the iframe `contentDocument` finished loading. | ||
* | ||
* @event ui.iframe.IframeView#loaded | ||
*/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
/** | ||
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
import ToolbarView from '../toolbar/toolbarview.js'; | ||
|
||
/** | ||
* The sticky toolbar view class. | ||
* | ||
* @memberOf ui.toolbar | ||
* @extends ui.toolbarView | ||
*/ | ||
|
||
export default class StickyToolbarView extends ToolbarView { | ||
constructor( model ) { | ||
super( model ); | ||
|
||
const bind = this.attributeBinder; | ||
|
||
/** | ||
* Indicates whether the toolbar is in the "sticky" state. | ||
* | ||
* @readonly | ||
* @observable | ||
* @member {Boolean} ui.button.StickyToolbarModel#isSticky | ||
*/ | ||
this.model.set( 'isSticky', false ); | ||
|
||
// Toggle class of the toolbar when "sticky" state changes in the model. | ||
this.template.attributes.class.push( bind.if( 'isSticky', 'ck-toolbar_sticky' ) ); | ||
} | ||
|
||
init() { | ||
super.init(); | ||
|
||
/** | ||
* A dummy element which visually fills the space as long as the | ||
* actual toolbar is sticky. It prevents flickering of the UI. | ||
* | ||
* @private | ||
* @type {HTMLElement} | ||
* @property _elementPlaceholder | ||
*/ | ||
this._elementPlaceholder = document.createElement( 'div' ); | ||
this._elementPlaceholder.classList.add( 'ck-toolbar__placeholder' ); | ||
this.element.parentNode.insertBefore( this._elementPlaceholder, this.element ); | ||
|
||
// Update sticky state of the toolbar as the window is being scrolled. | ||
this.listenTo( window, 'scroll', () => { | ||
this._checkIfShouldBeSticky(); | ||
} ); | ||
|
||
// Synchronize with `model.isActive` because sticking an inactive toolbar is pointless. | ||
this.model.on( 'change:isActive', ( evt, name, value ) => { | ||
if ( value ) { | ||
this._checkIfShouldBeSticky(); | ||
} else { | ||
this._detach(); | ||
} | ||
} ); | ||
} | ||
|
||
/** | ||
* Analyzes the environment to decide whether the toolbar should | ||
* be sticky or not. Then, it uses {@link _stick} and {@link _detach} | ||
* methods to manage the state of the toolbar. | ||
* | ||
* @protected | ||
*/ | ||
_checkIfShouldBeSticky() { | ||
const rectElement = this.model.isSticky ? | ||
this._elementPlaceholder : this.element; | ||
const rect = rectElement.getBoundingClientRect(); | ||
|
||
if ( rect.top < 0 && this.model.isActive ) { | ||
this._stick( rect ); | ||
} else { | ||
this._detach(); | ||
} | ||
} | ||
|
||
/** | ||
* Sticks the toolbar to the top edge of the viewport simulating | ||
* CSS position:sticky. Also see {@link _detach}. | ||
* | ||
* TODO: Possibly replaced by CSS in the future | ||
* http://caniuse.com/#feat=css-sticky | ||
* | ||
* @protected | ||
* @param {Object} regionRect An output of getBoundingClientRect native DOM method. | ||
*/ | ||
_stick( regionRect ) { | ||
// Setup placeholder. | ||
Object.assign( this._elementPlaceholder.style, { | ||
display: 'block', | ||
height: regionRect.height + 'px' | ||
} ); | ||
|
||
// Stick the top region. | ||
Object.assign( this.element.style, { | ||
// Compensate 1px border which is added when becoming "sticky". | ||
width: regionRect.width + 2 + 'px', | ||
marginLeft: -window.scrollX - 1 + 'px' | ||
} ); | ||
|
||
this.model.isSticky = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like the view changing the model. This in fact isn't needed in the model at all. This is view's property (internal at this point). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is a thing of a model because of: this.template.attributes.class.push( bind.if( 'isSticky', 'ck-toolbar_sticky' ) ); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a super bad smell for me unfortunately ;/. If we need to add something to the model only to make bindings work, it's wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PS. Note that |
||
} | ||
|
||
/** | ||
* Detaches the toolbar from the top edge of the viewport. | ||
* See {@link _stick}. | ||
* | ||
* @protected | ||
*/ | ||
_detach() { | ||
// Release the placeholder. | ||
Object.assign( this._elementPlaceholder.style, { | ||
display: 'none' | ||
} ); | ||
|
||
// Detach the top region. | ||
Object.assign( this.element.style, { | ||
width: 'auto', | ||
marginLeft: 'auto' | ||
} ); | ||
|
||
this.model.isSticky = false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/** | ||
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md. | ||
*/ | ||
|
||
/* bender-tags: ui, iframe */ | ||
|
||
'use strict'; | ||
|
||
import IframeView from '/ckeditor5/ui/iframe/iframeview.js'; | ||
import Model from '/ckeditor5/ui/model.js'; | ||
|
||
describe( 'IframeView', () => { | ||
let model, view; | ||
|
||
beforeEach( () => { | ||
model = new Model( { | ||
width: 100, | ||
height: 200 | ||
} ); | ||
|
||
view = new IframeView( model ); | ||
|
||
view.init(); | ||
} ); | ||
|
||
describe( 'constructor', () => { | ||
it( 'creates view element from the template', () => { | ||
expect( view.element.classList.contains( 'ck-reset-all' ) ).to.be.true; | ||
expect( view.element.attributes.getNamedItem( 'sandbox' ).value ).to.equal( 'allow-same-origin allow-scripts' ); | ||
} ); | ||
} ); | ||
|
||
describe( 'init', () => { | ||
it( 'returns promise', () => { | ||
view = new IframeView( model ); | ||
|
||
expect( view.init() ).to.be.an.instanceof( Promise ); | ||
} ); | ||
|
||
it( 'returns promise which is resolved when iframe finished loading', () => { | ||
view = new IframeView( model ); | ||
|
||
const promise = view.init().then( () => { | ||
expect( view.element.contentDocument.readyState ).to.equal( 'complete' ); | ||
} ); | ||
|
||
// Moving iframe into DOM trigger creation of a document inside iframe. | ||
document.body.appendChild( view.element ); | ||
|
||
return promise; | ||
} ); | ||
} ); | ||
|
||
describe( 'loaded event', () => { | ||
it( 'is fired when frame finished loading', ( done ) => { | ||
view = new IframeView( model ); | ||
|
||
view.on( 'loaded', () => { | ||
done(); | ||
} ); | ||
|
||
view.init(); | ||
|
||
// Moving iframe into DOM trigger creation of a document inside iframe. | ||
document.body.appendChild( view.element ); | ||
} ); | ||
} ); | ||
} ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | ||
// For licensing, see LICENSE.md or http://ckeditor.com/license | ||
|
||
@include ck-editor { | ||
.ck-toolbar.ck-toolbar_sticky { | ||
position: fixed; | ||
top: 0; | ||
border: 1px solid ck-border-color(); | ||
background: ck-color( 'foreground' ); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | ||
// For licensing, see LICENSE.md or http://ckeditor.com/license | ||
|
||
@import 'components/stickytoolbar'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where's the placeholder removed?