Browse Source

Merge branch 'master' into t/297

Aleksander Nowodzinski 8 years ago
parent
commit
93b4638d39

+ 110 - 0
packages/ckeditor5-ui/docs/_snippets/examples/bootstrap-ui-inner.html

@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
+		<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
+		<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
+		<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
+	</head>
+	<body>
+		<!-- The outermost cotainer of the editor. -->
+		<div class="ck-editor">
+			<!-- The toolbar of the editor. -->
+			<div class="btn-toolbar" role="toolbar" aria-label="Editor toolbar">
+				<!-- The headings dropdown. -->
+				<div class="btn-group mr-2" role="group" aria-label="Headings">
+					<div class="dropdown" id="heading">
+					  <button class="btn btn-primary btn-sm dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span>Headings</span></button>
+					  <div class="dropdown-menu" aria-labelledby="heading-button"></div>
+					</div>
+				</div>
+
+				<!-- Basic styles buttons. -->
+				<div class="btn-group mr-2" role="group" aria-label="Basic styles">
+					<button type="button" class="btn btn-primary btn-sm" id="bold">B</button>
+					<button type="button" class="btn btn-primary btn-sm" id="italic">I</button>
+					<button type="button" class="btn btn-primary btn-sm" id="underline">U</button>
+				</div>
+
+				<!-- Undo and redo buttons. -->
+				<div class="btn-group mr-2" role="group" aria-label="Undo">
+					<button type="button" class="btn btn-primary btn-sm" id="undo">&#x2190;</button>
+					<button type="button" class="btn btn-primary btn-sm" id="redo">&#x2192;</button>
+				</div>
+			</div>
+
+			<!-- The container containing data of the editor. -->
+			<div id="editor">
+				<h2>Custom UI</h2>
+				<p>
+					This editor uses <b>Bootstrap</b> components to build the user interface.
+				</p>
+			</div>
+		</div>
+
+		<button type="button" id="toggle-readonly">Toggle editor read&#x2013;only</button>
+
+		<style>
+			body {
+				padding: 0 .2em;
+			}
+
+			/* Let's give the editor some space and limits using a border. */
+			.ck-editor {
+				margin: 0 0 1em;
+				border: 1px solid rgba( 0, 0, 0, .1 );
+				border-radius: 4px;
+			}
+
+			/* Adding internal spacing, border and background to the toolbar.  */
+			.ck-editor .btn-toolbar {
+				padding: .5rem;
+				background: #f7f7f9;
+				border-bottom: 1px solid rgba( 0, 0, 0, .1 );
+			}
+
+			/* Tweaking the editable area for better readability. */
+			.ck-editor .ck-editor__editable {
+				padding: 2em 2em 1em;
+				overflow: auto;
+				height: 300px;
+			}
+
+			/* When in read–only mode, the editable should fade out. */
+			.ck-editor .ck-editor__editable:not([contenteditable]) {
+				background: #fafafa;
+				color: #777;
+			}
+
+			/* Make sure the headings dropdown button does not change its size
+			as different headings are selected */
+			.ck-editor .dropdown-toggle span {
+				display: inline-block;
+				width: 100px;
+				text-align: left;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				vertical-align: bottom;
+			}
+
+			/* Make the headings dropdown items visually distinctive. */
+			.ck-editor .heading-item_heading1 { font-size: 1.5em; }
+			.ck-editor .heading-item_heading2 { font-size: 1.3em; }
+			.ck-editor .heading-item_heading3 { font-size: 1.1em; }
+
+			.ck-editor [class*="heading-item_"] {
+				line-height: 22px;
+				padding: 10px;
+			}
+
+			.ck-editor [class*="heading-item_heading"] {
+			  font-weight: bold;
+			}
+
+			/* Give the basic styles buttons some icon–like shape */
+			.ck-editor #bold { font-weight: bold; }
+			.ck-editor #italic { font-style: italic; }
+			.ck-editor #underline { text-decoration: underline; }
+		</style>
+	</body>
+</html>

+ 203 - 0
packages/ckeditor5-ui/docs/_snippets/examples/bootstrap-ui-inner.js

@@ -0,0 +1,203 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals $, window, console:false */
+
+// Basic classes to create an editor.
+import StandardEditor from '@ckeditor/ckeditor5-core/src/editor/standardeditor';
+import InlineEditableUIView from '@ckeditor/ckeditor5-ui/src/editableui/inline/inlineeditableuiview';
+import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
+import ElementReplacer from '@ckeditor/ckeditor5-utils/src/elementreplacer';
+
+// Basic features that every editor should enable.
+import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
+import Enter from '@ckeditor/ckeditor5-enter/src/enter';
+import Typing from '@ckeditor/ckeditor5-typing/src/typing';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import UndoEngine from '@ckeditor/ckeditor5-undo/src/undoengine';
+
+// Basic features to associated with the edited content.
+import BoldEngine from '@ckeditor/ckeditor5-basic-styles/src/boldengine';
+import ItalicEngine from '@ckeditor/ckeditor5-basic-styles/src/italicengine';
+import UnderlineEngine from '@ckeditor/ckeditor5-basic-styles/src/underlineengine';
+import HeadingEngine from '@ckeditor/ckeditor5-heading/src/headingengine';
+
+// Extending the StandardEditor, which brings lots of essential API.
+export default class BootstrapEditor extends StandardEditor {
+	constructor( element, config ) {
+		super( element, config );
+
+		// Create the ("main") root element of the model tree.
+		this.document.createRoot();
+
+		// Use the HTML data processor in this editor.
+		this.data.processor = new HtmlDataProcessor();
+
+		// This editor uses a single editable view in DOM.
+		this.editable = new InlineEditableUIView( this.locale );
+
+		// A helper to easily replace the editor#element with editor.editable#element.
+		this._elementReplacer = new ElementReplacer();
+	}
+
+	destroy() {
+		// When destroyed, editor sets the output of editor#getData() into editor#element...
+		this.updateEditorElement();
+
+		// ...and restores editor#element.
+		this._elementReplacer.restore();
+
+		return super.destroy();
+	}
+
+	static create( element, config ) {
+		return new Promise( resolve => {
+			const editor = new this( element, config );
+			const editable = editor.editable;
+
+			resolve(
+				editor.initPlugins()
+					// Initialize the editable view in DOM first.
+					.then( () => editable.init() )
+					// Replace the editor#element with editor.editable#element.
+					.then( () => editor._elementReplacer.replace( element, editable.element ) )
+					// Handle the UI of the editor.
+					.then( () => {
+						// Create an editing root in the editing layer. It will correspond with the
+						// document root created in the constructor().
+						const editingRoot = editor.editing.createRoot( 'div' );
+
+						// Bind the basic attributes of the editable in DOM with the editing layer.
+						editable.bind( 'isReadOnly' ).to( editingRoot );
+						editable.bind( 'isFocused' ).to( editor.editing.view );
+						editable.name = editingRoot.rootName;
+
+						// Setup the external Bootstrap UI so it works with the editor.
+						setupButtons( editor );
+						setupHeadingDropdown( editor );
+
+						// Tell the world that the UI of the editor is ready to use.
+						editor.fire( 'uiReady' );
+					} )
+					// Bind the editor editing layer to the editable in DOM.
+					.then( () => editor.editing.view.attachDomRoot( editable.element ) )
+					.then( () => editor.loadDataFromEditorElement() )
+					// Fire the events that announce that the editor is complete and ready to use.
+					.then( () => {
+						editor.fire( 'dataReady' );
+						editor.fire( 'ready' );
+					} )
+					.then( () => editor )
+			);
+		} );
+	}
+}
+
+// This function activates Bold, Italic, Underline, Undo and Redo buttons in the toolbar.
+function setupButtons( editor ) {
+	[ 'bold', 'italic', 'underline', 'undo', 'redo' ].forEach( commandName => {
+		// Retrieve the editor command corresponding with the id of the button in DOM.
+		const command = editor.commands.get( commandName );
+
+		// Retrieve the jQuery object corresponding with the button in DOM.
+		const button = $( `#${ commandName }` );
+
+		// Clicking on the buttons should execute the editor command...
+		button.click( () => editor.execute( commandName ) );
+
+		// ...but it should not steal the focus so the editing is uninterrupted.
+		button.mousedown( evt => evt.preventDefault() );
+
+		// Commands can become disabled, e.g. when the editor is read-only.
+		// Make sure the buttons reflect this state change.
+		command.on( 'change:isEnabled', onIsEnabledChange );
+		onIsEnabledChange();
+
+		// Bold, Italic and Underline commands have a value that changes
+		// when the selection starts in an element the command creates.
+		// The button should indicate that e.g. editing text which is already bold.
+		if ( !new Set( [ 'undo', 'redo' ] ).has( commandName ) ) {
+			command.on( 'change:value', onValueChange );
+			onValueChange();
+		}
+
+		function onValueChange() {
+			button.toggleClass( 'active', command.value );
+		}
+
+		function onIsEnabledChange() {
+			button.attr( 'disabled', () => !command.isEnabled );
+		}
+	} );
+}
+
+// This function activates the headings dropdown in the toolbar.
+function setupHeadingDropdown( editor ) {
+	const menu = $( '.ck-editor .dropdown-menu' );
+	const button = $( '.ck-editor .dropdown-toggle' );
+
+	// Create a dropdown menu entry for each heading configuration option.
+	editor.config.get( 'heading.options' ).map( option => {
+		// Retrieve the editor command corresponding with the configuration option.
+		const command = editor.commands.get( option.modelElement );
+
+		// Create the menu item DOM element.
+		const menuItem = $(
+			`<a href="#" class="dropdown-item heading-item_${ option.modelElement }">` +
+				`${ option.title }` +
+			'</a>' );
+
+		// Upon click, the dropdown menua item should execute the command and focus
+		// the editing view to keep the editing process uninterrupted.
+		menuItem.click( () => {
+			editor.execute( option.modelElement );
+			editor.editing.view.focus();
+		} );
+
+		menu.append( menuItem );
+
+		// Make sure the dropdown and its items reflect the state of the
+		// currently active command.
+		command.on( 'change:value', onValueChange );
+		onValueChange();
+
+		// Heading commands can become disabled, e.g. when the editor is read-only.
+		// Make sure the UI reflects this state change.
+		command.on( 'change:isEnabled', onIsEnabledChange );
+		onIsEnabledChange();
+
+		function onValueChange() {
+			if ( command.value ) {
+				button.children( ':first' ).text( option.title );
+			}
+
+			menuItem.toggleClass( 'active', command.value );
+		}
+
+		function onIsEnabledChange() {
+			button.attr( 'disabled', () => !command.isEnabled );
+		}
+	} );
+}
+
+// Finally, create the BootstrapEditor instance with a selected set of features.
+BootstrapEditor
+	.create( $( '#editor' ).get( 0 ), {
+		plugins: [
+			Clipboard, Enter, Typing, Paragraph,
+			BoldEngine, ItalicEngine, UnderlineEngine, HeadingEngine, UndoEngine
+		]
+	} )
+	.then( editor => {
+		window.editor = editor;
+
+		$( '#toggle-readonly' ).on( 'click', () => {
+			editor.isReadOnly = !editor.isReadOnly;
+		} );
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );
+

+ 9 - 0
packages/ckeditor5-ui/docs/_snippets/examples/bootstrap-ui.html

@@ -0,0 +1,9 @@
+<iframe src="%BASE_PATH%/snippets/examples/bootstrap-ui-inner/snippet.html" id="snippet-bootstrap-ui-inner"></iframe>
+
+<style>
+	#snippet-bootstrap-ui-inner {
+		width: 100%;
+		height: 420px;
+		border: 0;
+	}
+</style>

+ 6 - 0
packages/ckeditor5-ui/docs/_snippets/examples/bootstrap-ui.js

@@ -0,0 +1,6 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* config { "dependencies": [ "examples/bootstrap-ui-inner" ] } */

+ 9 - 0
packages/ckeditor5-ui/docs/examples/custom-ui.md

@@ -0,0 +1,9 @@
+---
+title: Custom UI (with Bootstrap)
+category: examples-framework
+order: 20
+---
+
+The editor below runs a completely custom user interface written in [Bootstrap](http://getbootstrap.com/), while the editing is provided by CKEditor 5. To learn more, check out the {@link framework/guides/external-ui detailed guide} on how to integrate an external UI with the editor.
+
+{@snippet examples/bootstrap-ui}

+ 375 - 0
packages/ckeditor5-ui/docs/framework/guides/external-ui.md

@@ -0,0 +1,375 @@
+---
+title: Third–party UI
+category: ui
+order: 20
+---
+
+CKEditor 5 is a modular editing framework and allows various flexible configurations. That includes the usage of a third–party user interface on top of the basic editor classes.
+
+In this guide, a [classic–like](https://www.npmjs.com/package/@ckeditor/ckeditor5-build-classic) editor will be bound to a completely separate UI created in [Bootstrap](http://getbootstrap.com/) providing the basic structure and toolbar items necessary to start editing.
+
+{@snippet examples/bootstrap-ui}
+
+## Readying the editor side
+
+The ready–to–use builds of CKEditor like {@link examples/builds/classic-editor Classic} or {@link examples/builds/inline-editor Inline} come with a dedicated, default user interface and theme. However, to create an editor instance bound to a Bootstrap UI, only a limited subset of features is required. Let's import them first:
+
+
+```js
+// Basic classes to create an editor.
+import StandardEditor from '@ckeditor/ckeditor5-core/src/editor/standardeditor';
+import InlineEditableUIView from '@ckeditor/ckeditor5-ui/src/editableui/inline/inlineeditableuiview';
+import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
+import ElementReplacer from '@ckeditor/ckeditor5-utils/src/elementreplacer';
+
+// Basic features that every editor should enable.
+import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
+import Enter from '@ckeditor/ckeditor5-enter/src/enter';
+import Typing from '@ckeditor/ckeditor5-typing/src/typing';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import UndoEngine from '@ckeditor/ckeditor5-undo/src/undoengine';
+
+// Basic features to associated with the edited content.
+import BoldEngine from '@ckeditor/ckeditor5-basic-styles/src/boldengine';
+import ItalicEngine from '@ckeditor/ckeditor5-basic-styles/src/italicengine';
+import UnderlineEngine from '@ckeditor/ckeditor5-basic-styles/src/underlineengine';
+import HeadingEngine from '@ckeditor/ckeditor5-heading/src/headingengine';
+```
+
+<info-box info>
+	Note that instead of {@link module:basic-styles/bold~Bold}, which is required for any editor with the default UI to work, just the {@link module:basic-styles/boldengine~BoldEngine} is imported. It provides the [engine](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine) features associated with editing any bold text but does not come with the actual UI.
+
+	Respectively, `ItalicEngine`, `UnderlineEngine`, `HeadingEngine` and `UndoEngine` are also imported.
+
+	This split between the engine and UI part of features is not perfect yet. At the current stage, the UI part introduces some vital functionality, such as keystroke definitions (e.g. <kbd>Ctrl</kbd>+<kbd>B</kbd> to "bold"). This means that by dropping the UI part of fetures you lose also keystrokes. We [plan to improve](https://github.com/ckeditor/ckeditor5/issues/488) this situation.
+</info-box>
+
+Having imported the very basic editor components, we can define the custom `BootstrapEditor` class that extends the {@link module:core/editor/standardeditor~StandardEditor `StandardEditor`}:
+
+```js
+// Extending the StandardEditor, which brings lots of essential API.
+export default class BootstrapEditor extends StandardEditor {
+	constructor( element, config ) {
+		super( element, config );
+
+		// Create the ("main") root element of the model tree.
+		this.document.createRoot();
+
+		// Use the HTML data processor in this editor.
+		this.data.processor = new HtmlDataProcessor();
+
+		// This editor uses a single editable view in DOM.
+		this.editable = new InlineEditableUIView( this.locale );
+
+		// A helper to easily replace the editor#element with editor.editable#element.
+		this._elementReplacer = new ElementReplacer();
+	}
+
+	destroy() {
+		// When destroyed, editor sets the output of editor#getData() into editor#element...
+		this.updateEditorElement();
+
+		// ...and restores editor#element.
+		this._elementReplacer.restore();
+
+		return super.destroy();
+	}
+
+	static create( element, config ) {
+		return new Promise( resolve => {
+			const editor = new this( element, config );
+			const editable = editor.editable;
+
+			resolve(
+				editor.initPlugins()
+					// Initialize the editable view in DOM first.
+					.then( () => editable.init() )
+					// Replace the editor#element with editor.editable#element.
+					.then( () => editor._elementReplacer.replace( element, editable.element ) )
+					// Handle the UI of the editor.
+					.then( () => {
+						// Create an editing root in the editing layer. It will correspond with the
+						// document root created in the constructor().
+						const editingRoot = editor.editing.createRoot( 'div' );
+
+						// Bind the basic attributes of the editable in DOM with the editing layer.
+						editable.bind( 'isReadOnly' ).to( editingRoot );
+						editable.bind( 'isFocused' ).to( editor.editing.view );
+						editable.name = editingRoot.rootName;
+
+						// Setup the external Bootstrap UI so it works with the editor. Check out the code samples bellow to learn more.
+						setupButtons( editor );
+						setupHeadingDropdown( editor );
+
+						// Tell the world that the UI of the editor is ready to use.
+						editor.fire( 'uiReady' );
+					} )
+					// Bind the editor editing layer to the editable in DOM.
+					.then( () => editor.editing.view.attachDomRoot( editable.element ) )
+					.then( () => editor.loadDataFromEditorElement() )
+					// Fire the events that announce that the editor is complete and ready to use.
+					.then( () => {
+						editor.fire( 'dataReady' );
+						editor.fire( 'ready' );
+					} )
+					.then( () => editor )
+			);
+		} );
+	}
+}
+```
+
+## Creating the Bootstrap UI
+
+Although the editor is ready to use, it's just a bare editable area, which isn't much use to the users. Let's give it the actual interface with the toolbar and buttons.
+
+<info-box hint>
+	Please refer to the Bootstrap [quick start guide](https://getbootstrap.com/docs/4.0/getting-started/introduction/) to learn how to include Bootstrap in your web page.
+</info-box>
+
+Having the Bootstrap framework loaded in the web page, we can define the actual UI of the editor in HTML:
+
+```html
+<!-- The outermost cotainer of the editor. -->
+<div class="ck-editor">
+	<!-- The toolbar of the editor. -->
+	<div class="btn-toolbar" role="toolbar" aria-label="Editor toolbar">
+		<!-- The headings dropdown. -->
+		<div class="btn-group mr-2" role="group" aria-label="Headings">
+			<div class="dropdown" id="heading">
+			  <button class="btn btn-primary btn-sm dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span>Headings</span></button>
+			  <div class="dropdown-menu" aria-labelledby="heading-button"></div>
+			</div>
+		</div>
+
+		<!-- Basic styles buttons. -->
+		<div class="btn-group mr-2" role="group" aria-label="Basic styles">
+			<button type="button" class="btn btn-primary btn-sm" id="bold">B</button>
+			<button type="button" class="btn btn-primary btn-sm" id="italic">I</button>
+			<button type="button" class="btn btn-primary btn-sm" id="underline">U</button>
+		</div>
+
+		<!-- Undo and redo buttons. -->
+		<div class="btn-group mr-2" role="group" aria-label="Undo">
+			<button type="button" class="btn btn-primary btn-sm" id="undo">&larr;</button>
+			<button type="button" class="btn btn-primary btn-sm" id="redo">&rarr;</button>
+		</div>
+	</div>
+
+	<!-- The container containing data of the editor. -->
+	<div id="editor">
+		<p>Hello world!</p>
+	</div>
+</div>
+```
+
+Although Bootstrap provides most of the CSS, it does not come with styles dedicated for [WYSIWYG](https://en.wikipedia.org/wiki/WYSIWYG) text editors and some tweaking is needed:
+
+```css
+/* Let's give the editor some space and limits using a border. */
+.ck-editor {
+	margin: 1em 0;
+	border: 1px solid rgba( 0, 0, 0, .1 );
+	border-radius: 4px;
+}
+
+/* Adding internal spacing, border and background to the toolbar.  */
+.ck-editor .btn-toolbar {
+	padding: .5rem;
+	background: #f7f7f9;
+	border-bottom: 1px solid rgba( 0, 0, 0, .1 );
+}
+
+/* Tweaking the editable area for better readability. */
+.ck-editor .ck-editor__editable {
+	padding: 2em 2em 1em;
+	overflow: auto;
+}
+
+/* When in read–only mode, the editable should fade out. */
+.ck-editor .ck-editor__editable:not([contenteditable]) {
+	background: #fafafa;
+	color: #777;
+}
+
+/* Make sure the headings dropdown button does not change its size
+as different headings are selected */
+.ck-editor .dropdown-toggle span {
+	display: inline-block;
+	width: 100px;
+	text-align: left;
+	overflow: hidden;
+	text-overflow: ellipsis;
+	vertical-align: bottom;
+}
+
+/* Make the headings dropdown items visually distinctive. */
+.ck-editor .heading-item_heading1 { font-size: 1.5em; }
+.ck-editor .heading-item_heading2 { font-size: 1.3em; }
+.ck-editor .heading-item_heading3 { font-size: 1.1em; }
+
+.ck-editor [class*="heading-item_"] {
+	line-height: 22px;
+	padding: 10px;
+}
+
+.ck-editor [class*="heading-item_heading"] {
+  font-weight: bold;
+}
+
+/* Give the basic styles buttons the icon–like look and feel. */
+.ck-editor #bold { font-weight: bold; }
+.ck-editor #italic { font-style: italic; }
+.ck-editor #underline { text-decoration: underline; }
+```
+
+## Binding the UI with the editor
+
+At this stage, we're about to bind the editor created at the very beginning of this guide with the Bootstrap UI defined in HTML. Almost every feature in the editor defines some command, e.g. {@link module:heading/headingcommand~HeadingCommand} or {@link module:undo/undocommand~UndoCommand}. Commands can be executed
+
+```js
+editor.exectute( 'undo' );
+```
+
+but they also come with default observable attributes like `value` and `isEnabled`. These are the entry points when it comes to creating a custom user interface because their values represent the actual state of the editor and can be followed in simple event listeners:
+
+```js
+const command = editor.commands.get( 'undo' );
+
+command.on( 'change:isEnabled', ( evt, name, isEnabled ) => {
+	if ( isEnabled ) {
+		console.log( 'Whoa, you can undo some stuff now.' );
+	} else {
+		console.log( 'There is nothing to undo in the editor.' );
+	}
+} );
+```
+
+<info-box hint>
+	To learn more about editor commands, check out the {@link module:core/command~Command} API. You can also `console.log` the {@link module:core/editor/editor~Editor#commands `editor.commands`} collection of a live editor to learn which commands it offers.
+</info-box>
+
+Now let's take a closer look on these two mysterious lines in the `BootstrapEditor#create()` :
+
+```js
+setupButtons( editor );
+setupHeadingDropdown( editor );
+```
+
+`setupButtons()` is a function that binds the Bootstrap toolbar buttons with the editor features. It activates the related editor commands upon clicking and makes the buttons listen to the state of the commands to update their CSS classes:
+
+```js
+// This function activates Bold, Italic, Underline, Undo and Redo buttons in the toolbar.
+function setupButtons( editor ) {
+	[ 'bold', 'italic', 'underline', 'undo', 'redo' ].forEach( commandName => {
+		// Retrieve the editor command corresponding with the id of the button in DOM.
+		const command = editor.commands.get( commandName );
+
+		// Retrieve the jQuery object corresponding with the button in DOM.
+		const button = $( `#${ commandName }` );
+
+		// Clicking on the buttons should execute the editor command...
+		button.click( () => editor.execute( commandName ) );
+
+		// ...but it should not steal the focus so the editing is uninterrupted.
+		button.mousedown( evt => evt.preventDefault() );
+
+		// Commands can become disabled, e.g. when the editor is read-only.
+		// Make sure the buttons reflect this state change.
+		command.on( 'change:isEnabled', onIsEnabledChange );
+		onIsEnabledChange();
+
+		// Bold, Italic and Underline commands have a value that changes
+		// when the selection starts in an element the command creates.
+		// The button should indicate that the user is editing a text which
+		// is already bold.
+		if ( !new Set( [ 'undo', 'redo' ] ).has( commandName ) ) {
+			command.on( 'change:value', onValueChange );
+			onValueChange();
+		}
+
+		function onValueChange() {
+			button.toggleClass( 'active', command.value );
+		}
+
+		function onIsEnabledChange() {
+			button.attr( 'disabled', () => !command.isEnabled );
+		}
+	} );
+}
+```
+
+The dropdown in the toolbar is a more complex case because first it must be populated with heading options for the users to select from. Then, clicking each option must execute a related heading command in the editor. Finally, the dropdown button and the dropdown menu items must reflect the state of the editor, e.g. when the selection lands in a heading, a proper menu item should become active and the button should show the name of the heading level.
+
+```js
+// This function activates the headings dropdown in the toolbar.
+function setupHeadingDropdown( editor ) {
+	const menu = $( '.ck-editor .dropdown-menu' );
+	const button = $( '.ck-editor .dropdown-toggle' );
+
+	// Create a dropdown menu entry for each heading configuration option.
+	editor.config.get( 'heading.options' ).map( option => {
+		// Retrieve the editor command corresponding with the configuration option.
+		const command = editor.commands.get( option.modelElement );
+		// Create the menu item DOM element.
+
+		const menuItem = $(
+			`<a href="#" class="dropdown-item heading-item_${ option.modelElement }">` +
+				`${ option.title }` +
+			'</a>' );
+
+		// Upon click, the dropdown menua item should execute the command and focus
+		// the editing view to keep the editing process uninterrupted.
+		menuItem.click( () => {
+			editor.execute( option.modelElement );
+			editor.editing.view.focus();
+		} );
+
+		menu.append( menuItem );
+
+		// Make sure the dropdown and its items reflect the state of the
+		// currently active command.
+		command.on( 'change:value', onValueChange );
+		onValueChange();
+
+		// Heading commands can become disabled, e.g. when the editor is read-only.
+		// Make sure the UI reflects this state change.
+		command.on( 'change:isEnabled', onIsEnabledChange );
+		onIsEnabledChange();
+
+		function onValueChange() {
+			if ( command.value ) {
+				button.children( ':first' ).text( option.title );
+			}
+
+			menuItem.toggleClass( 'active', command.value );
+		}
+
+		function onIsEnabledChange() {
+			button.attr( 'disabled', () => !command.isEnabled );
+		}
+	} );
+}
+```
+
+## Running the editor
+
+When the editor class and the user interface are ready, it's time to run the editor. Just make sure all the plugins are loaded and the right DOM element is passed to `BootstrapEditor#create`:
+
+```js
+BootstrapEditor
+	.create( $( '#editor' ).get( 0 ), {
+		plugins: [
+			Clipboard, Enter, Typing, Paragraph,
+			BoldEngine, ItalicEngine, UnderlineEngine, HeadingEngine, UndoEngine
+		]
+	} )
+	.then( editor => {
+		window.editor = editor;
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );
+```
+
+Once everything is working as expected, you may want to create a custom build of your editor to ship it across the applications. To learn more check out the {@link builds/guides/development/custom-builds custom builds} guide.

+ 1 - 1
packages/ckeditor5-ui/package.json

@@ -12,7 +12,7 @@
   },
   "devDependencies": {
     "@ckeditor/ckeditor5-basic-styles": "^0.9.0",
-    "@ckeditor/ckeditor5-dev-lint": "^3.1.0",
+    "@ckeditor/ckeditor5-dev-lint": "^3.1.1",
     "@ckeditor/ckeditor5-editor-classic": "^0.8.0",
     "@ckeditor/ckeditor5-engine": "^0.11.0",
     "@ckeditor/ckeditor5-enter": "^0.10.0",