8
0
Просмотр исходного кода

Merge tag 'v1.0.0-beta.1' into stable

Release: v1.0.0-beta.1.
Piotrek Koszuliński 7 лет назад
Родитель
Сommit
3062599ab8
100 измененных файлов с 2123 добавлено и 870 удалено
  1. 1 1
      packages/ckeditor5-ui/.eslintrc.js
  2. 15 11
      packages/ckeditor5-ui/.travis.yml
  3. 48 2
      packages/ckeditor5-ui/CHANGELOG.md
  4. 1 1
      packages/ckeditor5-ui/LICENSE.md
  5. 3 1
      packages/ckeditor5-ui/README.md
  6. 8 3
      packages/ckeditor5-ui/docs/_snippets/examples/bootstrap-ui-inner.html
  7. 210 107
      packages/ckeditor5-ui/docs/_snippets/examples/bootstrap-ui-inner.js
  8. 1 1
      packages/ckeditor5-ui/docs/_snippets/examples/bootstrap-ui.js
  9. 1 1
      packages/ckeditor5-ui/docs/api/ui.md
  10. 217 100
      packages/ckeditor5-ui/docs/framework/guides/external-ui.md
  11. 1 1
      packages/ckeditor5-ui/lang/translations/ast.po
  12. 1 1
      packages/ckeditor5-ui/lang/translations/da.po
  13. 1 1
      packages/ckeditor5-ui/lang/translations/de.po
  14. 1 1
      packages/ckeditor5-ui/lang/translations/el.po
  15. 1 1
      packages/ckeditor5-ui/lang/translations/en-au.po
  16. 1 1
      packages/ckeditor5-ui/lang/translations/en.po
  17. 1 1
      packages/ckeditor5-ui/lang/translations/eo.po
  18. 14 0
      packages/ckeditor5-ui/lang/translations/es.po
  19. 14 0
      packages/ckeditor5-ui/lang/translations/et.po
  20. 14 0
      packages/ckeditor5-ui/lang/translations/eu.po
  21. 1 1
      packages/ckeditor5-ui/lang/translations/fi.po
  22. 1 1
      packages/ckeditor5-ui/lang/translations/fr.po
  23. 1 1
      packages/ckeditor5-ui/lang/translations/gl.po
  24. 1 1
      packages/ckeditor5-ui/lang/translations/hr.po
  25. 1 1
      packages/ckeditor5-ui/lang/translations/hu.po
  26. 1 1
      packages/ckeditor5-ui/lang/translations/it.po
  27. 1 1
      packages/ckeditor5-ui/lang/translations/ja.po
  28. 1 1
      packages/ckeditor5-ui/lang/translations/km.po
  29. 1 1
      packages/ckeditor5-ui/lang/translations/kn.po
  30. 1 1
      packages/ckeditor5-ui/lang/translations/ku.po
  31. 1 1
      packages/ckeditor5-ui/lang/translations/nb.po
  32. 14 0
      packages/ckeditor5-ui/lang/translations/nl.po
  33. 1 1
      packages/ckeditor5-ui/lang/translations/pt-br.po
  34. 1 1
      packages/ckeditor5-ui/lang/translations/pt.po
  35. 1 1
      packages/ckeditor5-ui/lang/translations/ro.po
  36. 14 0
      packages/ckeditor5-ui/lang/translations/ru.po
  37. 1 1
      packages/ckeditor5-ui/lang/translations/sk.po
  38. 14 0
      packages/ckeditor5-ui/lang/translations/sq.po
  39. 1 1
      packages/ckeditor5-ui/lang/translations/sv.po
  40. 1 1
      packages/ckeditor5-ui/lang/translations/tr.po
  41. 14 0
      packages/ckeditor5-ui/lang/translations/ug.po
  42. 1 1
      packages/ckeditor5-ui/lang/translations/uk.po
  43. 1 1
      packages/ckeditor5-ui/lang/translations/zh-cn.po
  44. 1 1
      packages/ckeditor5-ui/lang/translations/zh.po
  45. 19 17
      packages/ckeditor5-ui/package.json
  46. 1 1
      packages/ckeditor5-ui/src/bindings/clickoutsidehandler.js
  47. 1 1
      packages/ckeditor5-ui/src/bindings/preventdefault.js
  48. 1 1
      packages/ckeditor5-ui/src/bindings/submithandler.js
  49. 128 0
      packages/ckeditor5-ui/src/button/button.jsdoc
  50. 28 126
      packages/ckeditor5-ui/src/button/buttonview.js
  51. 7 5
      packages/ckeditor5-ui/src/componentfactory.js
  52. 22 0
      packages/ckeditor5-ui/src/dropdown/button/dropdownbutton.jsdoc
  53. 82 0
      packages/ckeditor5-ui/src/dropdown/button/dropdownbuttonview.js
  54. 219 0
      packages/ckeditor5-ui/src/dropdown/button/splitbuttonview.js
  55. 0 49
      packages/ckeditor5-ui/src/dropdown/createdropdown.js
  56. 0 51
      packages/ckeditor5-ui/src/dropdown/dropdownmodel.jsdoc
  57. 27 0
      packages/ckeditor5-ui/src/dropdown/dropdownpanelfocusable.jsdoc
  58. 32 4
      packages/ckeditor5-ui/src/dropdown/dropdownpanelview.js
  59. 100 21
      packages/ckeditor5-ui/src/dropdown/dropdownview.js
  60. 0 120
      packages/ckeditor5-ui/src/dropdown/list/createlistdropdown.js
  61. 0 33
      packages/ckeditor5-ui/src/dropdown/list/listdropdownmodel.jsdoc
  62. 0 26
      packages/ckeditor5-ui/src/dropdown/list/listdropdownview.jsdoc
  63. 243 0
      packages/ckeditor5-ui/src/dropdown/utils.js
  64. 3 1
      packages/ckeditor5-ui/src/editableui/editableuiview.js
  65. 1 1
      packages/ckeditor5-ui/src/editableui/inline/inlineeditableuiview.js
  66. 36 15
      packages/ckeditor5-ui/src/editorui/boxed/boxededitoruiview.js
  67. 3 1
      packages/ckeditor5-ui/src/editorui/editoruiview.js
  68. 1 1
      packages/ckeditor5-ui/src/focuscycler.js
  69. 41 5
      packages/ckeditor5-ui/src/icon/iconview.js
  70. 1 1
      packages/ckeditor5-ui/src/iframe/iframeview.js
  71. 23 3
      packages/ckeditor5-ui/src/inputtext/inputtextview.js
  72. 3 1
      packages/ckeditor5-ui/src/label/labelview.js
  73. 2 1
      packages/ckeditor5-ui/src/labeledinput/labeledinputview.js
  74. 1 1
      packages/ckeditor5-ui/src/list/listitemview.js
  75. 4 1
      packages/ckeditor5-ui/src/list/listview.js
  76. 1 1
      packages/ckeditor5-ui/src/model.js
  77. 1 1
      packages/ckeditor5-ui/src/notification/notification.js
  78. 7 3
      packages/ckeditor5-ui/src/panel/balloon/balloonpanelview.js
  79. 3 2
      packages/ckeditor5-ui/src/panel/balloon/contextualballoon.js
  80. 3 1
      packages/ckeditor5-ui/src/panel/sticky/stickypanelview.js
  81. 5 5
      packages/ckeditor5-ui/src/template.js
  82. 20 20
      packages/ckeditor5-ui/src/toolbar/balloon/balloontoolbar.js
  83. 1 1
      packages/ckeditor5-ui/src/toolbar/enabletoolbarkeyboardfocus.js
  84. 3 3
      packages/ckeditor5-ui/src/toolbar/normalizetoolbarconfig.js
  85. 1 1
      packages/ckeditor5-ui/src/toolbar/toolbarseparatorview.js
  86. 32 2
      packages/ckeditor5-ui/src/toolbar/toolbarview.js
  87. 3 1
      packages/ckeditor5-ui/src/tooltip/tooltipview.js
  88. 16 3
      packages/ckeditor5-ui/src/view.js
  89. 2 2
      packages/ckeditor5-ui/src/viewcollection.js
  90. 1 1
      packages/ckeditor5-ui/tests/_utils-tests/utils.js
  91. 1 1
      packages/ckeditor5-ui/tests/_utils/utils.js
  92. 1 1
      packages/ckeditor5-ui/tests/bindings/clickoutsidehandler.js
  93. 1 1
      packages/ckeditor5-ui/tests/bindings/preventdefault.js
  94. 1 1
      packages/ckeditor5-ui/tests/bindings/submithandler.js
  95. 18 10
      packages/ckeditor5-ui/tests/button/buttonview.js
  96. 8 2
      packages/ckeditor5-ui/tests/componentfactory.js
  97. 47 0
      packages/ckeditor5-ui/tests/dropdown/button/dropdownbuttonview.js
  98. 234 0
      packages/ckeditor5-ui/tests/dropdown/button/splitbuttonview.js
  99. 0 64
      packages/ckeditor5-ui/tests/dropdown/createdropdown.js
  100. 54 1
      packages/ckeditor5-ui/tests/dropdown/dropdownpanelview.js

+ 1 - 1
packages/ckeditor5-ui/.eslintrc.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 15 - 11
packages/ckeditor5-ui/.travis.yml

@@ -1,24 +1,28 @@
 sudo: required
 dist: trusty
 addons:
+  firefox: "latest"
   apt:
     sources:
-      - google-chrome
+    - google-chrome
     packages:
-      - google-chrome-stable
+    - google-chrome-stable
 language: node_js
 node_js:
-  - "6"
+- '6'
 cache:
-  - node_modules
+- node_modules
 before_install:
-  - export DISPLAY=:99.0
-  - sh -e /etc/init.d/xvfb start
+- export DISPLAY=:99.0
+- sh -e /etc/init.d/xvfb start
 install:
-  - npm install @ckeditor/ckeditor5-dev-tests
-  - ckeditor5-dev-tests-install-dependencies
+- npm install @ckeditor/ckeditor5-dev-tests
+- ckeditor5-dev-tests-install-dependencies
 script:
-  - ckeditor5-dev-tests-travis
+- ckeditor5-dev-tests-travis
 after_success:
-  - codeclimate-test-reporter < coverage/lcov.info
-  - ckeditor5-dev-tests-save-revision
+- ckeditor5-dev-tests-save-revision
+env:
+  global:
+  - secure: W6VrPcKacfXYH/P5qjowRpGcq8UHwBpKIub7Tfj7pGaTfQHrUMbPb4jHJLQYd335eiIyVWzmwatYhRJwB5wtaPkfcByCqRqwXQsl3nkmEZYclQdhUgjhCJEPQlu93XNWkaITQnXaJzlJ5etzgyQBKVh+bmaTITCOli5rdLqO7Pge00boTDRevLZ+T6Fw4iNVwSPAwLpbgYfR7pujqPu6kulDrMSOKWcaMImuEGq9Bvps2LieF9KzvoNBCgoleZd+tFFtTvyQ1eE/UeTFQD8GXdKs7fSjj3evuyAm9FWqMBL+zT1EDJoTy4WljLJPwt6JRMazFSp9+j/0KNPuzksLTduGKAxjcc5OzbLxV3Ckh3A//szazAkgrJlK9BNYVLqj9lBns0nL2QbcyR8bY1w8mKVKIXVoCMAxuPWBYbEKKdODDUQXGbz/4wKxx2Ar0boo27v2ewBzo6BmwQYt7X4oTQTfkP7rpUHorKS9ztjHR0FiBAqLN4TQ0nCVqE93ajoGt5o9NO/ZTQbggMptym4JCkJle17K16kNPB96gkQkCIoa20n7/okkNclTl6g9NdD3oeWQMXz4S5Jx8lc3I/9kK5JqZL8KCTFZ//9UNuf6xaSf8g52usl+Kyv+A12u34SJ06n0KvmoA5aQfFHs4rioKL20e7RSI3edZqwp+THGTuk=
+  - secure: O0yOs6WG5YnrBbx7qyeG7sXsZW5pJN0ZvF/sZgHLWEZ4UYIxAGyW1FkM2v0nUroNo18v2X8IDaTOuSxBRXlJUlG8nPElHTIh/pJcVIXzDuVEW1sO4cQcqlUNAgIAK9g9y4aWBH0JXhSFeg1FwRP5Jjp73t9th7tMTjW3/qqOl+1rAjj6zXC+DAQEt8551ue2NZC5OEuIoPtfk57FBe1oAWyRSJIHNR5dj10E9JXlrdVJzcCFF25ikYQbhjl3RpIiFAdaKKK68GyPpx5hkb0rBDHyHcIYSanFZSY2cAKkhdKrjFqWN97scQEK3NtLepOt6cKtH69KG0mYgyKvkIQgoyXo9RDW3NUEO4mQwd2ADOBRwvda5ekFdaQtCr1Dznuw+wrNeHAg/uI/qBBJYJ3ZuZFcD5DTg/zbPtOQzZb/LuhJuOYMVBvQyG4X6IUfbK3yLooo8gZjVWoxyb4u9S/fe55gArzFE5aWbjBDdTNw5qVDu2qd21yd9B6ol6QupT+aCRoKBtgUEU1VeUWdVs+RkLdi+f+kJ11FPA/RwG4D6EBgTd4c1JRHkUxSQNVvkPM7lb/x+IoHtOakjQTVUE/C459KigW+kD4iMlUPrFHND0tC34K2E67wWV1O7KfStD84NqgFwX5V/48EUSrqyePpMegOIet6D36JXRh+NssU5rU=

+ 48 - 2
packages/ckeditor5-ui/CHANGELOG.md

@@ -1,6 +1,52 @@
 Changelog
 =========
 
+## [1.0.0-beta.1](https://github.com/ckeditor/ckeditor5-ui/compare/v1.0.0-alpha.2...v1.0.0-beta.1) (2018-03-15)
+
+### Features
+
+* Implemented the `IconView#fillColor` observable which fills child `.ck-icon__fill` paths  with the color (see [ckeditor/ckeditor5-theme-lark#148](https://github.com/ckeditor/ckeditor5-theme-lark/issues/148)). ([728a691](https://github.com/ckeditor/ckeditor5-ui/commit/728a691))
+* Initial implementation of the `ButtonDropdownView`. Closes [#333](https://github.com/ckeditor/ckeditor5-ui/issues/333). ([6e9c6e4](https://github.com/ckeditor/ckeditor5-ui/commit/6e9c6e4))
+
+  Also:
+
+  * Allowed vertical layout of the `ToolbarView` thanks to the `#isVertical` attribute.
+  * Implemented `ToolbarView#className` attribute.
+  * Implemented `DropdownView#isEnabled` attribute along with the CSS class binding.
+* Updated UI components to bring the support for the refreshed Lark theme (see [ckeditor/ckeditor5#645](https://github.com/ckeditor/ckeditor5/issues/645)). ([623d536](https://github.com/ckeditor/ckeditor5-ui/commit/623d536))
+
+### Bug fixes
+
+* Button icon styles should not touch the dropdown's arrow. Closes [ckeditor/ckeditor5#831](https://github.com/ckeditor/ckeditor5/issues/831). ([507549f](https://github.com/ckeditor/ckeditor5-ui/commit/507549f))
+
+### Other changes
+
+* Added a CSS class to the SplitButtonView when the arrow is on (see [ckeditor/ckeditor5-theme-lark#134](https://github.com/ckeditor/ckeditor5-theme-lark/issues/134)). ([d490d61](https://github.com/ckeditor/ckeditor5-ui/commit/d490d61))
+* Added the `.ck-content` CSS class to the `EditableUIView` in order to simplify styling the editor content. Closes [#176](https://github.com/ckeditor/ckeditor5-ui/issues/176). ([f38ae70](https://github.com/ckeditor/ckeditor5-ui/commit/f38ae70))
+* Align feature class naming to a new scheme. ([1c500f6](https://github.com/ckeditor/ckeditor5-ui/commit/1c500f6))
+* Aligned code to changes (`config.lang` to `config.languages`). Part of the Translation Service v2 ([ckeditor/ckeditor5#624](https://github.com/ckeditor/ckeditor5/issues/624)). ([876f681](https://github.com/ckeditor/ckeditor5-ui/commit/876f681))
+* ComponentFactory.names() will return original component names (instead of normalized names). Closes [#376](https://github.com/ckeditor/ckeditor5-ui/issues/376). ([b6b39d7](https://github.com/ckeditor/ckeditor5-ui/commit/b6b39d7))
+* CSS classes should be prefixed with `ck-` instead of `cke-`. Closes [#112](https://github.com/ckeditor/ckeditor5-ui/issues/112). ([7973f83](https://github.com/ckeditor/ckeditor5-ui/commit/7973f83))
+* Introduced `SplitButtonView` and new dropdown creation helpers (`createDropdown()`, `addListToDropdown()` and `addToolbarToDropdown()`) Closes [#344](https://github.com/ckeditor/ckeditor5-ui/issues/344). Closes [#356](https://github.com/ckeditor/ckeditor5-ui/issues/356). ([0f26ca8](https://github.com/ckeditor/ckeditor5-ui/commit/0f26ca8))
+* Migrated the package styles from SASS to PostCSS to bring theme support and avoid duplicates in some editor builds. Closes [#144](https://github.com/ckeditor/ckeditor5-ui/issues/144). Closes [ckeditor/ckeditor5#420](https://github.com/ckeditor/ckeditor5/issues/420). ([f152dfc](https://github.com/ckeditor/ckeditor5-ui/commit/f152dfc))
+* Moved ck-button-icon mixin from ckeditor5-theme-lark. ([8757d27](https://github.com/ckeditor/ckeditor5-ui/commit/8757d27))
+* Removed the `.ck-editor-toolbar` and `.ck-editor-toolbar-container` classes (see [ckeditor/ckeditor5-theme-lark#135](https://github.com/ckeditor/ckeditor5-theme-lark/issues/135)). ([352d056](https://github.com/ckeditor/ckeditor5-ui/commit/352d056))
+* Rename `ContextualToolbar` to `BalloonToolbar`.  Closes [ckeditor/ckeditor5#550](https://github.com/ckeditor/ckeditor5/issues/550). ([28f59df](https://github.com/ckeditor/ckeditor5-ui/commit/28f59df))
+* The `closeDropdownOnBlur()` helper should use `clickOutsideHandler()`. Decorated the `View#render()` method. Closes [#311](https://github.com/ckeditor/ckeditor5-ui/issues/311). ([269e97b](https://github.com/ckeditor/ckeditor5-ui/commit/269e97b))
+* Updated translations. ([f657be2](https://github.com/ckeditor/ckeditor5-ui/commit/f657be2))
+
+### BREAKING CHANGES
+
+* Renamed `contextual/contextualtoolbar~ContextualToolbar` to `balloon/balloontoolbar~BalloonToolbar`.
+* Renamed `contextualToolbar` configuration option to `balloonToolbar`.
+* Removed `DropdownModel` interface – you can use dropdown properties directly now. See [#356](https://github.com/ckeditor/ckeditor5-ui/issues/356).
+* Removed `createButtonDropdown()` and `ButtonDropdownView`. See [#356](https://github.com/ckeditor/ckeditor5-ui/issues/356).
+* Removed `createListDropdown()` and `ListDropdownView`. See [#356](https://github.com/ckeditor/ckeditor5-ui/issues/356).
+* The DOM structure of the dropdown component has changed. Please refer to the documentation to find out more.
+* Basic properties of the balloon panel component have changed (i.e. the location of the arrow, the default positions), which may have an impact on existing integrations.
+* The styles are no longer developed in SASS which means the `.scss` files became unavailable. Please refer to the [Theme Customization](https://docs.ckeditor.com/ckeditor5/latest/framework/guides/ui/theme-customization.html) guide to learn more about migration to PostCSS.
+
+
 ## [1.0.0-alpha.2](https://github.com/ckeditor/ckeditor5-ui/compare/v1.0.0-alpha.1...v1.0.0-alpha.2) (2017-11-14)
 
 ### Bug fixes
@@ -16,7 +62,7 @@ Changelog
 
    Additionally, from now on `View#setTemplate()` and `View#extendTemplate()` methods are recommended as a shorthand for `view.template = new Template( { ... } )` and `Template.extend( view.template )`.
 
-    Please refer to the updated [documentation](https://docs.ckeditor.com/ckeditor5/latest/framework/guides/architecture/intro.html#UI-library) to learn more.
+    Please refer to the updated [documentation](https://docs.ckeditor.com/ckeditor5/latest/framework/guides/architecture/intro.html#ui-library) to learn more.
 * Removed legacy `width` and `height` attributes from the `BoxedEditorUIView`. Closes [#25](https://github.com/ckeditor/ckeditor5-ui/issues/25). ([ffa419a](https://github.com/ckeditor/ckeditor5-ui/commit/ffa419a))
 * The `ComponentFactory` should be case-insensitive. Closes [#324](https://github.com/ckeditor/ckeditor5-ui/issues/324). ([94417e9](https://github.com/ckeditor/ckeditor5-ui/commit/94417e9))
 * Updated translations. ([186f365](https://github.com/ckeditor/ckeditor5-ui/commit/186f365))
@@ -65,7 +111,7 @@ refer to the `KeystrokeHandler` helper to learn more.
 * `ContextualToolbar` should have proper editor toolbar styling. Closes [#230](https://github.com/ckeditor/ckeditor5-ui/issues/230). ([4e2ee36](https://github.com/ckeditor/ckeditor5-ui/commit/4e2ee36))
 * BalloonPanelView should prevent native #selectstart event. Closes [#243](https://github.com/ckeditor/ckeditor5-ui/issues/243). ([cba3fb1](https://github.com/ckeditor/ckeditor5-ui/commit/cba3fb1))
 * Button tooltip should not look blurry on low-DPI screens. Closes [#142](https://github.com/ckeditor/ckeditor5-ui/issues/142). Closes [#133](https://github.com/ckeditor/ckeditor5-ui/issues/133). ([a497aff](https://github.com/ckeditor/ckeditor5-ui/commit/a497aff))
-* Clicking and dragging in a drop-down panel should not break the selection. Closes [#228](https://github.com/ckeditor/ckeditor5-ui/issues/228). ([6066a09](https://github.com/ckeditor/ckeditor5-ui/commit/6066a09))
+* Clicking and dragging in a dropdown panel should not break the selection. Closes [#228](https://github.com/ckeditor/ckeditor5-ui/issues/228). ([6066a09](https://github.com/ckeditor/ckeditor5-ui/commit/6066a09))
 * Contextual toolbar should re–position correctly on window scroll. Closes [#227](https://github.com/ckeditor/ckeditor5-ui/issues/227). ([e5ea25f](https://github.com/ckeditor/ckeditor5-ui/commit/e5ea25f))
 * `DropdownView` should open upon arrow down key press. Closes [#249](https://github.com/ckeditor/ckeditor5-ui/issues/249). ([c1e6afc](https://github.com/ckeditor/ckeditor5-ui/commit/c1e6afc))
 * List items should handle Enter and Space key press when focused. Closes [#153](https://github.com/ckeditor/ckeditor5-ui/issues/153). ([403b64a](https://github.com/ckeditor/ckeditor5-ui/commit/403b64a))

+ 1 - 1
packages/ckeditor5-ui/LICENSE.md

@@ -2,7 +2,7 @@ Software License Agreement
 ==========================
 
 **CKEditor 5 UI Framework** – https://github.com/ckeditor/ckeditor5-ui <br>
-Copyright (c) 2003-2017, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2018, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
 
 Licensed under the terms of any of the following licenses at your choice:
 

+ 3 - 1
packages/ckeditor5-ui/README.md

@@ -4,7 +4,9 @@ CKEditor 5 UI framework
 [![Join the chat at https://gitter.im/ckeditor/ckeditor5](https://badges.gitter.im/ckeditor/ckeditor5.svg)](https://gitter.im/ckeditor/ckeditor5?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-ui.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-ui)
 [![Build Status](https://travis-ci.org/ckeditor/ckeditor5-ui.svg?branch=master)](https://travis-ci.org/ckeditor/ckeditor5-ui)
-[![Test Coverage](https://codeclimate.com/github/ckeditor/ckeditor5-ui/badges/coverage.svg)](https://codeclimate.com/github/ckeditor/ckeditor5-ui/coverage)
+[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=d3hvenZqQVZERFQ5d09FWXdyT0ozVXhLaVltRFRjTTUyZGpvQWNmWVhUUT0tLUZqNlJ1YWRUd0RvdEVOaEptM1B2Q0E9PQ==--c9d3dee40b9b4471ff3fb516d9ecf8d09292c7e0)](https://www.browserstack.com/automate/public-build/d3hvenZqQVZERFQ5d09FWXdyT0ozVXhLaVltRFRjTTUyZGpvQWNmWVhUUT0tLUZqNlJ1YWRUd0RvdEVOaEptM1B2Q0E9PQ==--c9d3dee40b9b4471ff3fb516d9ecf8d09292c7e0)
+[![Coverage Status](https://coveralls.io/repos/github/ckeditor/ckeditor5-ui/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5-ui?branch=master)
+<br>
 [![Dependency Status](https://david-dm.org/ckeditor/ckeditor5-ui/status.svg)](https://david-dm.org/ckeditor/ckeditor5-ui)
 [![devDependency Status](https://david-dm.org/ckeditor/ckeditor5-ui/dev-status.svg)](https://david-dm.org/ckeditor/ckeditor5-ui?type=dev)
 

+ 8 - 3
packages/ckeditor5-ui/docs/_snippets/examples/bootstrap-ui-inner.html

@@ -52,7 +52,7 @@
 			/* 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: 1px solid hsla(0, 0%, 0%, 0.1);
 				border-radius: 4px;
 			}
 
@@ -60,7 +60,7 @@
 			.ck-editor .btn-toolbar {
 				padding: .5rem;
 				background: #f7f7f9;
-				border-bottom: 1px solid rgba( 0, 0, 0, .1 );
+				border-bottom: 1px solid hsla(0, 0%, 0%, 0.1);
 			}
 
 			/* Tweaking the editable area for better readability. */
@@ -73,7 +73,7 @@
 			/* When in read–only mode, the editable should fade out. */
 			.ck-editor .ck-editor__editable:not([contenteditable]) {
 				background: #fafafa;
-				color: #777;
+				color: hsl(0, 0%, 47%);
 			}
 
 			/* Make sure the headings dropdown button does not change its size
@@ -105,6 +105,11 @@
 			.ck-editor #bold { font-weight: bold; }
 			.ck-editor #italic { font-style: italic; }
 			.ck-editor #underline { text-decoration: underline; }
+
+			/* https://github.com/ckeditor/ckeditor5/issues/903 */
+			.ck-editor .ck-content > :first-child {
+				margin-top: 0;
+			}
 		</style>
 	</body>
 </html>

+ 210 - 107
packages/ckeditor5-ui/docs/_snippets/examples/bootstrap-ui-inner.js

@@ -1,42 +1,69 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, 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 Editor from '@ckeditor/ckeditor5-core/src/editor/editor';
+import EditorUIView from '@ckeditor/ckeditor5-ui/src/editorui/editoruiview';
+import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
+import ComponentFactory from '@ckeditor/ckeditor5-ui/src/componentfactory';
 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';
 
+// Interfaces to extend basic Editor API.
+import DataApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/dataapimixin';
+import ElementApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/elementapimixin';
+
+// Helper function for adding interfaces to the Editor class.
+import mix from '@ckeditor/ckeditor5-utils/src/mix';
+
+// Helper function that gets data from HTML element that the Editor is attached to.
+import getDataFromElement from '@ckeditor/ckeditor5-utils/src/dom/getdatafromelement';
+
+// Helper function that binds editor with HTMLForm element.
+import attachToForm from '@ckeditor/ckeditor5-core/src/editor/utils/attachtoform';
+
 // 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';
+import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting';
 
 // 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';
+import BoldEditing from '@ckeditor/ckeditor5-basic-styles/src/bold/boldediting';
+import ItalicEditing from '@ckeditor/ckeditor5-basic-styles/src/italic/italicediting';
+import UnderlineEditing from '@ckeditor/ckeditor5-basic-styles/src/underline/underlineediting';
+import HeadingEditing from '@ckeditor/ckeditor5-heading/src/headingediting';
 
-// Extending the StandardEditor, which brings lots of essential API.
-export default class BootstrapEditor extends StandardEditor {
+// The easy image integration.
+import EasyImage from '@ckeditor/ckeditor5-easy-image/src/easyimage';
+import { CS_CONFIG } from '@ckeditor/ckeditor5-cloudservices/tests/_utils/cloudservices-config';
+
+// Extending the Editor class, which brings base editor API.
+export default class BootstrapEditor extends Editor {
 	constructor( element, config ) {
-		super( element, config );
+		super( config );
 
-		// Create the ("main") root element of the model tree.
-		this.document.createRoot();
+		// Remember the element the editor is created with.
+		this.element = element;
 
 		// 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 );
+		// Create the ("main") root element of the model tree.
+		this.model.document.createRoot();
+
+		// The UI layer of the editor.
+		this.ui = new BootstrapEditorUI( this );
+
+		// When editor#element is a textarea inside a form element
+		// then content of this textarea will be updated on form submit.
+		attachToForm( this );
 
 		// A helper to easily replace the editor#element with editor.editable#element.
 		this._elementReplacer = new ElementReplacer();
@@ -44,46 +71,38 @@ export default class BootstrapEditor extends StandardEditor {
 
 	destroy() {
 		// When destroyed, editor sets the output of editor#getData() into editor#element...
-		this.updateEditorElement();
+		this.updateElement();
 
-		// ...and restores editor#element.
+		// ...and restores the original editor#element...
 		this._elementReplacer.restore();
 
+		// ...and destroys the UI.
+		this.ui.destroy();
+
 		return super.destroy();
 	}
 
 	static create( element, config ) {
 		return new Promise( resolve => {
 			const editor = new this( element, config );
-			const editable = editor.editable;
+			const editable = editor.ui.view.editable;
 
 			resolve(
 				editor.initPlugins()
-					// Render the editable view in DOM first.
-					.then( () => editable.render() )
-					// 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' );
+						// Initialize the UI first. See the BootstrapEditorUI class to learn more.
+						editor.ui.init();
 
-						// 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 );
+						// Replace the editor#element with editor.editable#element.
+						editor._elementReplacer.replace( element, editable.element );
 
 						// 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() )
+					// Fill the editable with the initial data.
+					.then( () => editor.data.init( getDataFromElement( element ) ) )
 					// Fire the events that announce that the editor is complete and ready to use.
 					.then( () => {
 						editor.fire( 'dataReady' );
@@ -95,100 +114,185 @@ export default class BootstrapEditor extends StandardEditor {
 	}
 }
 
-// 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 );
+// Mixing interfaces, which extends basic editor API.
+mix( BootstrapEditor, DataApiMixin );
+mix( BootstrapEditor, ElementApiMixin );
 
-		// Retrieve the jQuery object corresponding with the button in DOM.
-		const button = $( `#${ commandName }` );
+// The class organizing the UI of the editor, binding it with existing Bootstrap elements in DOM.
+class BootstrapEditorUI {
+	constructor( editor ) {
+		this.editor = editor;
 
-		// Clicking on the buttons should execute the editor command...
-		button.click( () => editor.execute( commandName ) );
+		// The global UI view of the editor. It aggregates various Bootstrap DOM elements.
+		const view = this.view = new EditorUIView( editor.locale );
 
-		// ...but it should not steal the focus so the editing is uninterrupted.
-		button.mousedown( evt => evt.preventDefault() );
+		// This is the main editor element in DOM.
+		view.element = $( '.ck-editor' );
 
-		// 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();
+		// This is the editable view in DOM. It will replace the data container in DOM.
+		view.editable = new InlineEditableUIView( editor.locale );
 
-		// 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();
-		}
+		// References to the dropdown elements for further usage. See #_setupBootstrapHeadingDropdown.
+		view.dropdownMenu = view.element.find( '.dropdown-menu' );
+		view.dropdownToggle = view.element.find( '.dropdown-toggle' );
 
-		function onValueChange() {
-			button.toggleClass( 'active', command.value );
-		}
+		// References to the toolbar buttons for further usage. See #_setupBootstrapToolbarButtons.
+		view.toolbarButtons = {};
+
+		[ 'bold', 'italic', 'underline', 'undo', 'redo' ].forEach( name => {
+			// Retrieve the jQuery object corresponding with the button in DOM.
+			view.toolbarButtons[ name ] = view.element.find( `#${ name }` );
+		} );
+
+		// Mandatory EditorUI interface components.
+		this.componentFactory = new ComponentFactory( editor );
+		this.focusTracker = new FocusTracker();
+	}
+
+	init() {
+		const editor = this.editor;
+		const view = this.view;
+
+		// Render the editable component in DOM first.
+		view.editable.render();
+
+		// Create an editing root in the editing layer. It will correspond with the
+		// document root created in the constructor().
+		const editingRoot = editor.editing.view.document.getRoot();
+
+		// Bind the basic attributes of the editable in DOM with the editing layer.
+		view.editable.bind( 'isReadOnly' ).to( editingRoot );
+		view.editable.bind( 'isFocused' ).to( editor.editing.view.document );
+		view.editable.name = editingRoot.rootName;
+
+		// Setup the existing, external Bootstrap UI so it works with the rest of the editor.
+		this._setupBootstrapToolbarButtons();
+		this._setupBootstrapHeadingDropdown();
+	}
+
+	destroy() {
+		this.view.editable.destroy();
+	}
+
+	// This method activates Bold, Italic, Underline, Undo and Redo buttons in the toolbar.
+	_setupBootstrapToolbarButtons() {
+		const editor = this.editor;
 
-		function onIsEnabledChange() {
-			button.attr( 'disabled', () => !command.isEnabled );
+		for ( const name in this.view.toolbarButtons ) {
+			// Retrieve the editor command corresponding with the id of the button in DOM.
+			const command = editor.commands.get( name );
+			const button = this.view.toolbarButtons[ name ];
+
+			// Clicking on the buttons should execute the editor command...
+			button.click( () => editor.execute( name ) );
+
+			// ...but it should not steal the focus so the editing is uninterrupted.
+			button.mousedown( evt => evt.preventDefault() );
+
+			const onValueChange = () => {
+				button.toggleClass( 'active', command.value );
+			};
+
+			const onIsEnabledChange = () => {
+				button.attr( 'disabled', () => !command.isEnabled );
+			};
+
+			// 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( name ) ) {
+				command.on( 'change:value', onValueChange );
+				onValueChange();
+			}
 		}
-	} );
-}
+	}
 
-// 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();
-		} );
+	// This method activates the headings dropdown in the toolbar.
+	_setupBootstrapHeadingDropdown() {
+		const editor = this.editor;
+		const dropdownMenu = this.view.dropdownMenu;
+		const dropdownToggle = this.view.dropdownToggle;
+
+		// Retrieve the editor commands for heading and paragraph.
+		const headingCommand = editor.commands.get( 'heading' );
+		const paragraphCommand = editor.commands.get( 'paragraph' );
+
+		// Create a dropdown menu entry for each heading configuration option.
+		editor.config.get( 'heading.options' ).map( option => {
+			// Check is options is paragraph or heading as their commands slightly differ.
+			const isParagraph = option.model === 'paragraph';
+
+			// Create the menu item DOM element.
+			const menuItem = $(
+				`<a href="#" class="dropdown-item heading-item_${ option.model }">` +
+					`${ option.title }` +
+				'</a>'
+			);
 
-		menu.append( menuItem );
+			// Upon click, the dropdown menu item should execute the command and focus
+			// the editing view to keep the editing process uninterrupted.
+			menuItem.click( () => {
+				const commandName = isParagraph ? 'paragraph' : 'heading';
+				const commandValue = isParagraph ? undefined : { value: option.model };
 
-		// Make sure the dropdown and its items reflect the state of the
-		// currently active command.
-		command.on( 'change:value', onValueChange );
-		onValueChange();
+				editor.execute( commandName, commandValue );
+				editor.editing.view.focus();
+			} );
 
-		// 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();
+			dropdownMenu.append( menuItem );
 
-		function onValueChange() {
-			if ( command.value ) {
-				button.children( ':first' ).text( option.title );
+			const command = isParagraph ? paragraphCommand : headingCommand;
+
+			// Make sure the dropdown and its items reflect the state of the
+			// currently active command.
+			const onValueChange = isParagraph ? onValueChangeParagraph : onValueChangeHeading;
+			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 onValueChangeHeading() {
+				const isActive = !isParagraph && command.value === option.model;
+
+				if ( isActive ) {
+					dropdownToggle.children( ':first' ).text( option.title );
+				}
+
+				menuItem.toggleClass( 'active', isActive );
 			}
 
-			menuItem.toggleClass( 'active', command.value );
-		}
+			function onValueChangeParagraph() {
+				if ( command.value ) {
+					dropdownToggle.children( ':first' ).text( option.title );
+				}
 
-		function onIsEnabledChange() {
-			button.attr( 'disabled', () => !command.isEnabled );
-		}
-	} );
+				menuItem.toggleClass( 'active', command.value );
+			}
+
+			function onIsEnabledChange() {
+				dropdownToggle.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
-		]
+			Clipboard, Enter, Typing, Paragraph, EasyImage,
+			BoldEditing, ItalicEditing, UnderlineEditing, HeadingEditing, UndoEditing
+		],
+		cloudServices: CS_CONFIG
 	} )
 	.then( editor => {
 		window.editor = editor;
@@ -200,4 +304,3 @@ BootstrapEditor
 	.catch( err => {
 		console.error( err.stack );
 	} );
-

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

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-ui/docs/api/ui.md

@@ -12,7 +12,7 @@ Together with the {@link api/engine editing engine} and the {@link api/core core
 
 ## Documentation
 
-See the introduction to the {@link framework/guides/architecture/intro#UI-library UI library's architecture}.
+See the introduction to the {@link framework/guides/architecture/intro#ui-library UI library's architecture}.
 
 You can also browse the API documentation of this package by using the module tree on the left.
 

+ 217 - 100
packages/ckeditor5-ui/docs/framework/guides/external-ui.md

@@ -5,62 +5,83 @@ order: 20
 
 # Third–party UI
 
-CKEditor 5 is a modular editing framework that allows for various flexible configurations. That includes the usage of a third–party user interface on top of the basic editor classes.
+CKEditor 5 is a modular editing framework that allows various flexible configurations. That includes the usage of a third–party user interface on top of the base 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.
+In this guide, a [classic–like](https://www.npmjs.com/package/@ckeditor/ckeditor5-build-classic) editor will be bound to a completely separate, existing 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. You need to import them first:
+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 a theme. However, to create an editor instance bound to a Bootstrap UI, only a limited subset of features is required. You need to import them first:
 
 
 ```js
 // Basic classes to create an editor.
-import StandardEditor from '@ckeditor/ckeditor5-core/src/editor/standardeditor';
+import Editor from '@ckeditor/ckeditor5-core/src/editor/editor';
+import EditorUIView from '@ckeditor/ckeditor5-ui/src/editorui/editoruiview';
+import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
+import ComponentFactory from '@ckeditor/ckeditor5-ui/src/componentfactory';
 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';
 
+// Interfaces to extend basic Editor API.
+import DataApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/dataapimixin';
+import ElementApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/elementapimixin';
+
+// Helper function for adding interfaces to the Editor class.
+import mix from '@ckeditor/ckeditor5-utils/src/mix';
+
+// Helper function that gets data from HTML element that the Editor is attached to.
+import getDataFromElement from '@ckeditor/ckeditor5-utils/src/dom/getdatafromelement';
+
+// Helper function that binds editor with HTMLForm element.
+import attachToForm from '@ckeditor/ckeditor5-core/src/editor/utils/attachtoform';
+
 // 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';
+import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoengine';
 
-// Basic features to be 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';
+// Basic features to associated with the edited content.
+import BoldEditing from '@ckeditor/ckeditor5-basic-styles/src/bold/boldediting';
+import ItalicEditing from '@ckeditor/ckeditor5-basic-styles/src/italic/italicediting';
+import UnderlineEditing from '@ckeditor/ckeditor5-basic-styles/src/underline/underlineediting';
+import HeadingEditing from '@ckeditor/ckeditor5-heading/src/heading/headingediting';
 ```
 
 <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.
+	Note that instead of {@link module:basic-styles/bold~Bold}, which loads default Bold UI and Bold editing feature, just the {@link module:basic-styles/bold/boldediting~BoldEditing} 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.
 
-	This split between the engine and the 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 features you also lose keystrokes. We [plan to improve](https://github.com/ckeditor/ckeditor5/issues/488) this situation.
+	Respectively, `ItalicEditing`, `UnderlineEditing`, `HeadingEditing` and `UndoEditing` are also imported.
 </info-box>
 
-Having imported the very basic editor components, you can define the custom `BootstrapEditor` class that extends the {@link module:core/editor/standardeditor~StandardEditor `StandardEditor`}:
+Having imported the very basic editor components, you can define the custom `BootstrapEditor` class that extends the {@link module:core/editor/editor~Editor `Editor`}:
 
 ```js
-// Extending the StandardEditor that brings lots of essential API.
-export default class BootstrapEditor extends StandardEditor {
+// Extending the Editor class, which brings base editor API.
+export default class BootstrapEditor extends Editor {
 	constructor( element, config ) {
-		super( element, config );
+		super( config );
 
-		// Create the ("main") root element of the model tree.
-		this.document.createRoot();
+		// Remember the element the editor is created with.
+		this.element = element;
 
 		// 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 );
+		// Create the ("main") root element of the model tree.
+		this.model.document.createRoot();
+
+		// The UI layer of the editor.
+		this.ui = new BootstrapEditorUI( this );
+
+		// When editor#element is a textarea inside a form element
+		// then content of this textarea will be updated on form submit.
+		attachToForm( this );
 
 		// A helper to easily replace the editor#element with editor.editable#element.
 		this._elementReplacer = new ElementReplacer();
@@ -68,46 +89,38 @@ export default class BootstrapEditor extends StandardEditor {
 
 	destroy() {
 		// When destroyed, editor sets the output of editor#getData() into editor#element...
-		this.updateEditorElement();
+		this.updateElement();
 
-		// ...and restores editor#element.
+		// ...and restores the original editor#element...
 		this._elementReplacer.restore();
 
+		// ...and destroys the UI.
+		this.ui.destroy();
+
 		return super.destroy();
 	}
 
 	static create( element, config ) {
 		return new Promise( resolve => {
 			const editor = new this( element, config );
-			const editable = editor.editable;
+			const editable = editor.ui.view.editable;
 
 			resolve(
 				editor.initPlugins()
-					// Render the editable view in DOM first.
-					.then( () => editable.render() )
-					// 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 to 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;
+						// Initialize the UI first. See the BootstrapEditorUI class to learn more.
+						editor.ui.init();
 
-						// Setup the external Bootstrap UI so it works with the editor. Check out the code samples below to learn more.
-						setupButtons( editor );
-						setupHeadingDropdown( editor );
+						// Replace the editor#element with editor.editable#element.
+						editor._elementReplacer.replace( element, editable.element );
 
 						// 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() )
+					// Fill the editable with the intial data.
+					.then( () => editor.data.init( getDataFromElement( element ) ) )
 					// Fire the events that announce that the editor is complete and ready to use.
 					.then( () => {
 						editor.fire( 'dataReady' );
@@ -118,6 +131,10 @@ export default class BootstrapEditor extends StandardEditor {
 		} );
 	}
 }
+
+// Mixing interfaces, which extends basic editor API.
+mix( BootstrapEditor, DataApiMixin );
+mix( BootstrapEditor, ElementApiMixin );
 ```
 
 ## Creating the Bootstrap UI
@@ -135,7 +152,7 @@ With the Bootstrap framework loaded in the web page, you can define the actual U
 <div class="ck-editor">
 	<!-- The toolbar of the editor. -->
 	<div class="btn-toolbar" role="toolbar" aria-label="Editor toolbar">
-		<!-- The headings drop-down. -->
+		<!-- 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>
@@ -170,15 +187,15 @@ Although Bootstrap provides most of the CSS, it does not come with styles dedica
 /* Give the editor some space and limits using a border. */
 .ck-editor {
 	margin: 1em 0;
-	border: 1px solid rgba( 0, 0, 0, .1 );
+	border: 1px solid hsla(0, 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 );
+	background: hsl(240, 14%, 97%);
+	border-bottom: 1px solid hsla(0, 0%, 0%, 0.1);
 }
 
 /* Tweaking the editable area for better readability. */
@@ -189,11 +206,11 @@ Although Bootstrap provides most of the CSS, it does not come with styles dedica
 
 /* When in read–only mode, the editable should fade out. */
 .ck-editor .ck-editor__editable:not([contenteditable]) {
-	background: #fafafa;
-	color: #777;
+	background: hsl(0, 0%, 98%);
+	color: hsl(0, 0%, 47%);
 }
 
-/* Make sure the headings drop-down button does not change its size
+/* Make sure the headings dropdown button does not change its size
 as different headings are selected. */
 .ck-editor .dropdown-toggle span {
 	display: inline-block;
@@ -204,7 +221,7 @@ as different headings are selected. */
 	vertical-align: bottom;
 }
 
-/* Make the headings drop-down items visually distinctive. */
+/* 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; }
@@ -226,13 +243,90 @@ as different headings are selected. */
 
 ## Binding the UI with the editor
 
-At this stage, you are 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:
+At this stage, we should bind the editor created at the very beginning of this guide with the Bootstrap UI defined in HTML. All the UI logic will be wrapped into a separate class matching the `EditorUI` {@link module:core/editor/editorui~EditorUI interface}. You may have noticed this line in the constructor of the `BootstrapEditor`:
+
+```js
+this.ui = new BootstrapEditorUI( this );
+```
+
+Let's define the `BootstrapEditorUI` then a have a closer look at the content of the class:
+
+```js
+// The class organizing the UI of the editor, binding it with existing Bootstrap elements in DOM.
+class BootstrapEditorUI {
+	constructor( editor ) {
+		this.editor = editor;
+
+		// The global UI view of the editor. It aggregates various Bootstrap DOM elements.
+		const view = this.view = new EditorUIView( editor.locale );
+
+		// This is the main editor element in DOM.
+		view.element = $( '.ck-editor' );
+
+		// This is the editable view in DOM. It will replace the data container in DOM.
+		view.editable = new InlineEditableUIView( editor.locale );
+
+		// References to the dropdown elements for further usage. See #_setupBootstrapHeadingDropdown.
+		view.dropdownMenu = view.element.find( '.dropdown-menu' );
+		view.dropdownToggle = view.element.find( '.dropdown-toggle' );
+
+		// References to the toolbar buttons for further usage. See #_setupBootstrapToolbarButtons.
+		view.toolbarButtons = {};
+
+		[ 'bold', 'italic', 'underline', 'undo', 'redo' ].forEach( name => {
+			// Retrieve the jQuery object corresponding with the button in DOM.
+			view.toolbarButtons[ name ] = view.element.find( `#${ name }` );
+		} );
+
+		// Mandatory EditorUI interface components.
+		this.componentFactory = new ComponentFactory( editor );
+		this.focusTracker = new FocusTracker();
+	}
+
+	init() {
+		const editor = this.editor;
+		const view = this.view;
+
+		// Render the editable component in DOM first.
+		view.editable.render();
+
+		// Create an editing root in the editing layer. It will correspond with the
+		// document root created in the constructor().
+		const editingRoot = editor.editing.view.document.getRoot();
+
+		// Bind the basic attributes of the editable in DOM with the editing layer.
+		view.editable.bind( 'isReadOnly' ).to( editingRoot );
+		view.editable.bind( 'isFocused' ).to( editor.editing.view.document );
+		view.editable.name = editingRoot.rootName;
+
+		// Setup the existing, external Bootstrap UI so it works with the rest of the editor.
+		this._setupBootstrapToolbarButtons();
+		this._setupBootstrapHeadingDropdown();
+	}
+
+	destroy() {
+		this.view.editable.destroy();
+	}
+
+	// This method activates Bold, Italic, Underline, Undo and Redo buttons in the toolbar.
+	_setupBootstrapToolbarButtons() {
+		...
+	}
+
+	// This method activates the headings dropdown in the toolbar.
+	_setupBootstrapHeadingDropdown() {
+		...
+	}
+}
+```
+
+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:
+But they also come with default observable properties 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' );
@@ -250,104 +344,127 @@ command.on( 'change:isEnabled', ( evt, name, isEnabled ) => {
 	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 take a closer look at these two mysterious lines in the `BootstrapEditor#create()` method:
+Knowing that, let's fill out the missing methods of the `BootstrapEditorUI`.
 
-```js
-setupButtons( editor );
-setupHeadingDropdown( editor );
-```
+### Binding the buttons to editor commands
 
-`setupButtons()` is a function that binds 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:
+`_setupBootstrapToolbarButtons()` is a method that binds Bootstrap toolbar buttons to the editor features (commands). It activates the corresponding 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 );
+// This method activates Bold, Italic, Underline, Undo and Redo buttons in the toolbar.
+_setupBootstrapToolbarButtons() {
+	const editor = this.editor;
 
-		// Retrieve the jQuery object corresponding with the button in DOM.
-		const button = $( `#${ commandName }` );
+	for ( const name in this.view.toolbarButtons ) {
+		// Retrieve the editor command corresponding with the id of the button in DOM.
+		const command = editor.commands.get( name );
+		const button = this.view.toolbarButtons[ name ];
 
-		// Clicking the buttons should execute the editor command...
-		button.click( () => editor.execute( commandName ) );
+		// Clicking on the buttons should execute the editor command...
+		button.click( () => editor.execute( name ) );
 
 		// ...but it should not steal the focus so the editing is uninterrupted.
 		button.mousedown( evt => evt.preventDefault() );
 
+		const onValueChange = () => {
+			button.toggleClass( 'active', command.value );
+		};
+
+		const onIsEnabledChange = () => {
+			button.attr( 'disabled', () => !command.isEnabled );
+		};
+
 		// 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 that 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 ) ) {
+		// 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( name ) ) {
 			command.on( 'change:value', onValueChange );
 			onValueChange();
 		}
-
-		function onValueChange() {
-			button.toggleClass( 'active', command.value );
-		}
-
-		function onIsEnabledChange() {
-			button.attr( 'disabled', () => !command.isEnabled );
-		}
-	} );
+	}
 }
 ```
 
-The drop-down 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 drop-down button and the drop-down menu items must reflect the state of the editor, for example, 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.
+### Binding the dropdown to the heading commands
+
+The dropdown in the toolbar is a more complex case.
+
+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, for example, 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 drop-down in the toolbar.
-function setupHeadingDropdown( editor ) {
-	const menu = $( '.ck-editor .dropdown-menu' );
-	const button = $( '.ck-editor .dropdown-toggle' );
+// This method activates the headings dropdown in the toolbar.
+_setupBootstrapHeadingDropdown() {
+	const editor = this.editor;
+	const dropdownMenu = this.view.dropdownMenu;
+	const dropdownToggle = this.view.dropdownToggle;
+
+	// Retrieve the editor commands for heading and paragraph.
+	const headingCommand = editor.commands.get( 'heading' );
+	const paragraphCommand = editor.commands.get( 'paragraph' );
 
-	// Create a drop-down menu entry for each heading configuration option.
+	// 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.
+		// Check is options is paragraph or heading as their commands slightly differ.
+		const isParagraph = option.model === 'paragraph';
 
+		// Create the menu item DOM element.
 		const menuItem = $(
-			`<a href="#" class="dropdown-item heading-item_${ option.modelElement }">` +
+			`<a href="#" class="dropdown-item heading-item_${ option.model }">` +
 				`${ option.title }` +
-			'</a>' );
+			'</a>'
+		);
 
-		// Upon clicking, the drop-down menu item should execute the command and focus
+		// Upon click, the dropdown menu item should execute the command and focus
 		// the editing view to keep the editing process uninterrupted.
 		menuItem.click( () => {
-			editor.execute( option.modelElement );
+			const commandName = isParagraph ? 'paragraph' : 'heading';
+			const commandValue = isParagraph ? undefined : { value: option.model };
+
+			editor.execute( commandName, commandValue );
 			editor.editing.view.focus();
 		} );
 
-		menu.append( menuItem );
+		dropdownMenu.append( menuItem );
+
+		const command = isParagraph ? paragraphCommand : headingCommand;
 
-		// Make sure the drop-down and its items reflect the state of the
+		// Make sure the dropdown and its items reflect the state of the
 		// currently active command.
+		const onValueChange = isParagraph ? onValueChangeParagraph : onValueChangeHeading;
 		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() {
+		function onValueChangeHeading() {
+			const isActive = !isParagraph && command.value === option.model;
+
+			if ( isActive ) {
+				dropdownToggle.children( ':first' ).text( option.title );
+			}
+
+			menuItem.toggleClass( 'active', isActive );
+		}
+
+		function onValueChangeParagraph() {
 			if ( command.value ) {
-				button.children( ':first' ).text( option.title );
+				dropdownToggle.children( ':first' ).text( option.title );
 			}
 
 			menuItem.toggleClass( 'active', command.value );
 		}
 
 		function onIsEnabledChange() {
-			button.attr( 'disabled', () => !command.isEnabled );
+			dropdownToggle.attr( 'disabled', () => !command.isEnabled );
 		}
 	} );
 }
@@ -355,14 +472,14 @@ function setupHeadingDropdown( editor ) {
 
 ## Running the editor
 
-When the editor class and the user interface are ready, it is time to run the editor. Just make sure all the plugins are loaded and the right DOM element is passed to `BootstrapEditor#create`:
+When the editor classes and the user interface are ready, it is 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
+			BoldEditing, ItalicEditing, UnderlineEditing, HeadingEditing, UndoEditing
 		]
 	} )
 	.then( editor => {

+ 1 - 1
packages/ckeditor5-ui/lang/translations/ast.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: ast\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/da.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: da\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/de.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: de\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/el.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: el\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/en-au.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: en_AU\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/en.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: \n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/eo.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: eo\n"

+ 14 - 0
packages/ckeditor5-ui/lang/translations/es.po

@@ -0,0 +1,14 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: es\n"
+"Language-Team: Spanish (https://www.transifex.com/ckeditor/teams/11143/es/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor, %0"
+msgstr "Editor de Texto Enriquecido, %0"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor"
+msgstr "Editor de Texto Enriquecido"

+ 14 - 0
packages/ckeditor5-ui/lang/translations/et.po

@@ -0,0 +1,14 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: et\n"
+"Language-Team: Estonian (https://www.transifex.com/ckeditor/teams/11143/et/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor, %0"
+msgstr "Tekstiredaktor, %0"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor"
+msgstr "Tekstiredaktor"

+ 14 - 0
packages/ckeditor5-ui/lang/translations/eu.po

@@ -0,0 +1,14 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: eu\n"
+"Language-Team: Basque (https://www.transifex.com/ckeditor/teams/11143/eu/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor, %0"
+msgstr "Testu aberastuaren editorea, %0"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor"
+msgstr "Testu aberastuaren editorea"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/fi.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: fi\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/fr.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: fr\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/gl.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: gl\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/hr.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: hr\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/hu.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: hu\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/it.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: it\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/ja.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: ja\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/km.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: km\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/kn.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: kn\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/ku.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: ku\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/nb.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: nb\n"

+ 14 - 0
packages/ckeditor5-ui/lang/translations/nl.po

@@ -0,0 +1,14 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: nl\n"
+"Language-Team: Dutch (https://www.transifex.com/ckeditor/teams/11143/nl/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor, %0"
+msgstr "Tekstbewerker, 0%"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor"
+msgstr "Tekstbewerker"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/pt-br.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: pt_BR\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/pt.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: pt\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/ro.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: ro\n"

+ 14 - 0
packages/ckeditor5-ui/lang/translations/ru.po

@@ -0,0 +1,14 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: ru\n"
+"Language-Team: Russian (https://www.transifex.com/ckeditor/teams/11143/ru/)\n"
+"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor, %0"
+msgstr "Редактор, %0"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor"
+msgstr "Редактор"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/sk.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: sk\n"

+ 14 - 0
packages/ckeditor5-ui/lang/translations/sq.po

@@ -0,0 +1,14 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: sq\n"
+"Language-Team: Albanian (https://www.transifex.com/ckeditor/teams/11143/sq/)\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor, %0"
+msgstr "Redaktues i Tekstit të Pasur, %0"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor"
+msgstr "Redaktues i Tekstit të Pasur"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/sv.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: sv\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/tr.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: tr\n"

+ 14 - 0
packages/ckeditor5-ui/lang/translations/ug.po

@@ -0,0 +1,14 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language: ug\n"
+"Language-Team: Uighur (https://www.transifex.com/ckeditor/teams/11143/ug/)\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor, %0"
+msgstr "تېكىست تەھرىرلىگۈچ، 0%"
+
+msgctxt "Title of the CKEditor5 editor."
+msgid "Rich Text Editor"
+msgstr "تېكىست تەھرىرلىگۈچ"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/uk.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: uk\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/zh-cn.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: zh_CN\n"

+ 1 - 1
packages/ckeditor5-ui/lang/translations/zh.po

@@ -1,4 +1,4 @@
-# Copyright (c) Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
 msgid ""
 msgstr ""
 "Language: zh_TW\n"

+ 19 - 17
packages/ckeditor5-ui/package.json

@@ -1,32 +1,34 @@
 {
   "name": "@ckeditor/ckeditor5-ui",
-  "version": "1.0.0-alpha.2",
+  "version": "1.0.0-beta.1",
   "description": "The UI framework and standard UI library of CKEditor 5.",
   "keywords": [
     "ckeditor5",
     "ckeditor5-lib"
   ],
   "dependencies": {
-    "@ckeditor/ckeditor5-core": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-theme-lark": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-utils": "^1.0.0-alpha.2"
+    "@ckeditor/ckeditor5-core": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-theme-lark": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-utils": "^1.0.0-beta.1"
   },
   "devDependencies": {
-    "@ckeditor/ckeditor5-basic-styles": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-editor-classic": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-engine": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-enter": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-heading": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-image": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-link": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-paragraph": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-typing": "^1.0.0-alpha.2",
-    "@ckeditor/ckeditor5-undo": "^1.0.0-alpha.2",
+    "@ckeditor/ckeditor5-basic-styles": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-cloudservices": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-editor-classic": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-engine": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-enter": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-easy-image": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-heading": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-image": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-link": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-paragraph": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-typing": "^1.0.0-beta.1",
+    "@ckeditor/ckeditor5-undo": "^1.0.0-beta.1",
     "del": "^2.2.0",
-    "eslint": "^4.8.0",
-    "eslint-config-ckeditor5": "^1.0.6",
+    "eslint": "^4.15.0",
+    "eslint-config-ckeditor5": "^1.0.7",
     "husky": "^0.14.3",
-    "lint-staged": "^4.2.3",
+    "lint-staged": "^6.0.0",
     "lodash-cli": "^4"
   },
   "engines": {

+ 1 - 1
packages/ckeditor5-ui/src/bindings/clickoutsidehandler.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-ui/src/bindings/preventdefault.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-ui/src/bindings/submithandler.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 128 - 0
packages/ckeditor5-ui/src/button/button.jsdoc

@@ -0,0 +1,128 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module ui/button/button
+ */
+
+/**
+ * The button interface. Implemented by, among others, {@link module:ui/button/buttonview~ButtonView},
+ * {@link module:ui/dropdown/button/splitbuttonview~SplitButtonView} and
+ * {@link module:ui/dropdown/button/dropdownbuttonview~DropdownButtonView}.
+ *
+ * @interface module:ui/button/button~Button
+ */
+
+/**
+ * The label of the button view visible to the user when {@link #withText} is `true`.
+ * It can also be used to create a {@link #tooltip}.
+ *
+ * @observable
+ * @member {String} #label
+ */
+
+/**
+ * (Optional) The keystroke associated with the button, i.e. <kbd>CTRL+B</kbd>,
+ * in the string format compatible with {@link module:utils/keyboard}.
+ *
+ * @observable
+ * @member {Boolean} #keystroke
+ */
+
+/**
+ * (Optional) Tooltip of the button, i.e. displayed when hovering the button with the mouse cursor.
+ *
+ * * If defined as a `Boolean` (e.g. `true`), then combination of `label` and `keystroke` will be set as a tooltip.
+ * * If defined as a `String`, tooltip will equal the exact text of that `String`.
+ * * If defined as a `Function`, `label` and `keystroke` will be passed to that function, which is to return
+ * a string with the tooltip text.
+ *
+ *		const view = new ButtonView( locale );
+ *		view.tooltip = ( label, keystroke ) => `A tooltip for ${ label } and ${ keystroke }.`
+ *
+ * @observable
+ * @default false
+ * @member {Boolean|String|Function} #tooltip
+ */
+
+/**
+ * (Optional) The position of the tooltip. See {@link module:ui/tooltip/tooltipview~TooltipView#position}
+ * to learn more about the available position values.
+ *
+ * **Note:** It makes sense only when the {@link #tooltip `tooltip` attribute} is defined.
+ *
+ * @observable
+ * @default 's'
+ * @member {'s'|'n'} #tooltipPosition
+ */
+
+/**
+ * The HTML type of the button. Default `button`.
+ *
+ * @observable
+ * @member {'button'|'submit'|'reset'|'menu'} #type
+ */
+
+/**
+ * Controls whether the button view is "on". It makes sense when a feature it represents
+ * is currently active, e.g. a bold button is "on" when the selection is in the bold text.
+ *
+ * To disable the button, use {@link #isEnabled} instead.
+ *
+ * @observable
+ * @default true
+ * @member {Boolean} #isOn
+ */
+
+/**
+ * Controls whether the button view is enabled, i.e. it can be clicked and execute an action.
+ *
+ * To change the "on" state of the button, use {@link #isOn} instead.
+ *
+ * @observable
+ * @default true
+ * @member {Boolean} #isEnabled
+ */
+
+/**
+ * Controls whether the button view is visible. Visible by default, buttons are hidden
+ * using a CSS class.
+ *
+ * @observable
+ * @default true
+ * @member {Boolean} #isVisible
+ */
+
+/**
+ * (Optional) Controls whether the label of the button is hidden (e.g. an icon–only button).
+ *
+ * @observable
+ * @default false
+ * @member {Boolean} #withText
+ */
+
+/**
+ * (Optional) An XML {@link module:ui/icon/iconview~IconView#content content} of the icon.
+ * When defined, an `iconView` should be added to the button.
+ *
+ * @observable
+ * @member {String} #icon
+ */
+
+/**
+ * (Optional) Controls the `tabindex` HTML attribute of the button. By default, the button is focusable
+ * but does not included in the <kbd>Tab</kbd> order.
+ *
+ * @observable
+ * @default -1
+ * @member {String} #tabindex
+ */
+
+/**
+ * Fired when the button view is clicked. It won't be fired when the button {@link #isEnabled}
+ * is `false`.
+ *
+ * @event execute
+ */

+ 28 - 126
packages/ckeditor5-ui/src/button/buttonview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -13,6 +13,8 @@ import TooltipView from '../tooltip/tooltipview';
 
 import { getEnvKeystrokeText } from '@ckeditor/ckeditor5-utils/src/keyboard';
 
+import '../../theme/components/button/button.css';
+
 /**
  * The button view class.
  *
@@ -30,6 +32,7 @@ import { getEnvKeystrokeText } from '@ckeditor/ckeditor5-utils/src/keyboard';
  *		document.body.append( view.element );
  *
  * @extends module:ui/view~View
+ * @implements module:ui/button/button~Button
  */
 export default class ButtonView extends View {
 	/**
@@ -40,118 +43,19 @@ export default class ButtonView extends View {
 
 		const bind = this.bindTemplate;
 
-		/**
-		 * The label of the button view visible to the user when {@link #withText} is `true`.
-		 * It can also be used to create a {@link #tooltip}.
-		 *
-		 * @observable
-		 * @member {String} #label
-		 */
-		this.set( 'label' );
-
-		/**
-		 * (Optional) The keystroke associated with the button, i.e. <kbd>CTRL+B</kbd>,
-		 * in the string format compatible with {@link module:utils/keyboard}.
-		 *
-		 * @observable
-		 * @member {Boolean} #keystroke
-		 */
+		// Implement the Button interface.
+		this.set( 'icon' );
+		this.set( 'isEnabled', true );
+		this.set( 'isOn', false );
+		this.set( 'isVisible', true );
 		this.set( 'keystroke' );
-
-		/**
-		 * (Optional) Tooltip of the button, i.e. displayed when hovering the button with the mouse cursor.
-		 *
-		 * * If defined as a `Boolean` (e.g. `true`), then combination of `label` and `keystroke` will be set as a tooltip.
-		 * * If defined as a `String`, tooltip will equal the exact text of that `String`.
-		 * * If defined as a `Function`, `label` and `keystroke` will be passed to that function, which is to return
-		 * a string with the tooltip text.
-		 *
-		 *		const view = new ButtonView( locale );
-		 *		view.tooltip = ( label, keystroke ) => `A tooltip for ${ label } and ${ keystroke }.`
-		 *
-		 * @observable
-		 * @default false
-		 * @member {Boolean|String|Function} #tooltip
-		 */
+		this.set( 'label' );
+		this.set( 'tabindex', -1 );
 		this.set( 'tooltip' );
-
-		/**
-		 * (Optional) The position of the tooltip. See {@link module:ui/tooltip/tooltipview~TooltipView#position}
-		 * to learn more about the available position values.
-		 *
-		 * **Note:** It makes sense only when the {@link #tooltip `tooltip` attribute} is defined.
-		 *
-		 * @observable
-		 * @default 's'
-		 * @member {'s'|'n'} #position
-		 */
 		this.set( 'tooltipPosition', 's' );
-
-		/**
-		 * The HTML type of the button. Default `button`.
-		 *
-		 * @observable
-		 * @member {'button'|'submit'|'reset'|'menu'} #type
-		 */
 		this.set( 'type', 'button' );
-
-		/**
-		 * Controls whether the button view is "on". It makes sense when a feature it represents
-		 * is currently active, e.g. a bold button is "on" when the selection is in the bold text.
-		 *
-		 * To disable the button, use {@link #isEnabled} instead.
-		 *
-		 * @observable
-		 * @member {Boolean} #isOn
-		 */
-		this.set( 'isOn', false );
-
-		/**
-		 * Controls whether the button view is enabled, i.e. it can be clicked and execute an action.
-		 *
-		 * To change the "on" state of the button, use {@link #isOn} instead.
-		 *
-		 * @observable
-		 * @member {Boolean} #isEnabled
-		 */
-		this.set( 'isEnabled', true );
-
-		/**
-		 * Controls whether the button view is visible. Visible by default, buttons are hidden
-		 * using a CSS class.
-		 *
-		 * @observable
-		 * @member {Boolean} #isVisible
-		 */
-		this.set( 'isVisible', true );
-
-		/**
-		 * (Optional) Controls whether the label of the button is hidden (e.g. an icon–only button).
-		 *
-		 * @observable
-		 * @member {Boolean} #withText
-		 */
 		this.set( 'withText', false );
 
-		/**
-		 * (Optional) An XML {@link module:ui/icon/iconview~IconView#content content} of the icon.
-		 * When defined, an {@link #iconView} will be added to the button.
-		 *
-		 * @observable
-		 * @member {String} #icon
-		 */
-		this.set( 'icon' );
-
-		/**
-		 * (Optional) Controls the `tabindex` HTML attribute of the button. By default, the button is focusable
-		 * but does not included in the <kbd>Tab</kbd> order.
-		 *
-		 * @observable
-		 * @default -1
-		 * @member {String} #tabindex
-		 */
-		this.set( 'tabindex', -1 );
-
 		/**
 		 * Collection of the child views inside of the button {@link #element}.
 		 *
@@ -176,6 +80,21 @@ export default class ButtonView extends View {
 		 */
 		this.labelView = this._createLabelView();
 
+		/**
+		 * The icon view of the button. Will be added to {@link #children} when the
+		 * {@link #icon icon attribute} is defined.
+		 *
+		 * @readonly
+		 * @member {module:ui/icon/iconview~IconView} #iconView
+		 */
+		this.iconView = new IconView();
+
+		this.iconView.extendTemplate( {
+			attributes: {
+				class: 'ck-button__icon'
+			}
+		} );
+
 		/**
 		 * Tooltip of the button bound to the template.
 		 *
@@ -192,13 +111,6 @@ export default class ButtonView extends View {
 			this._getTooltipString.bind( this )
 		);
 
-		/**
-		 * (Optional) The icon view of the button. Only present when the {@link #icon icon attribute} is defined.
-		 *
-		 * @readonly
-		 * @member {module:ui/icon/iconview~IconView} #iconView
-		 */
-
 		this.setTemplate( {
 			tag: 'button',
 
@@ -234,13 +146,6 @@ export default class ButtonView extends View {
 				} )
 			}
 		} );
-
-		/**
-		 * Fired when the button view is clicked. It won't be fired when the button {@link #isEnabled}
-		 * is `false`.
-		 *
-		 * @event execute
-		 */
 	}
 
 	/**
@@ -250,11 +155,8 @@ export default class ButtonView extends View {
 		super.render();
 
 		if ( this.icon ) {
-			const iconView = this.iconView = new IconView();
-
-			iconView.bind( 'content' ).to( this, 'icon' );
-
-			this.children.add( iconView );
+			this.iconView.bind( 'content' ).to( this, 'icon' );
+			this.children.add( this.iconView );
 		}
 
 		this.children.add( this.tooltipView );

+ 7 - 5
packages/ckeditor5-ui/src/componentfactory.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -59,10 +59,12 @@ export default class ComponentFactory {
 	/**
 	 * Returns an iterator of registered component names. Names are returned in lower case.
 	 *
-	 * @returns {Iterator.<String>}
+	 * @returns {Iterable.<String>}
 	 */
 	* names() {
-		yield* this._components.keys();
+		for ( const value of this._components.values() ) {
+			yield value.originalName;
+		}
 	}
 
 	/**
@@ -87,7 +89,7 @@ export default class ComponentFactory {
 			);
 		}
 
-		this._components.set( getNormalized( name ), callback );
+		this._components.set( getNormalized( name ), { callback, originalName: name } );
 	}
 
 	/**
@@ -115,7 +117,7 @@ export default class ComponentFactory {
 			);
 		}
 
-		return this._components.get( getNormalized( name ) )( this.editor.locale );
+		return this._components.get( getNormalized( name ) ).callback( this.editor.locale );
 	}
 
 	/**

+ 22 - 0
packages/ckeditor5-ui/src/dropdown/button/dropdownbutton.jsdoc

@@ -0,0 +1,22 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module ui/dropdown/button/dropdownbutton
+ */
+
+/**
+ * The dropdown button interface.
+ *
+ * @interface module:ui/dropdown/button/dropdownbutton~DropdownButton
+ * @extends module:ui/button/button~Button
+ */
+
+/**
+ * Fired when the dropdown should be opened.
+ * It will not be fired when the button {@link #isEnabled is disabled}.
+ *
+ * @event open
+ */

+ 82 - 0
packages/ckeditor5-ui/src/dropdown/button/dropdownbuttonview.js

@@ -0,0 +1,82 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module ui/dropdown/button/dropdownbuttonview
+ */
+
+import ButtonView from '../../button/buttonview';
+
+import dropdownArrowIcon from '../../../theme/icons/dropdown-arrow.svg';
+import IconView from '../../icon/iconview';
+
+/**
+ * The default dropdown button view class.
+ *
+ *		const view = new DropdownButtonView();
+ *
+ *		view.set( {
+ *			label: 'A button',
+ *			keystroke: 'Ctrl+B',
+ *			tooltip: true
+ *		} );
+ *
+ *		view.render();
+ *
+ *		document.body.append( view.element );
+ *
+ * Also see the {@link module:ui/dropdown/utils~createDropdown `createDropdown()` util}.
+ *
+ * @implements module:ui/dropdown/button/dropdownbutton~DropdownButton
+ * @extends module:ui/button/buttonview~ButtonView
+ */
+export default class DropdownButtonView extends ButtonView {
+	/**
+	 * @inheritDoc
+	 */
+	constructor( locale ) {
+		super( locale );
+
+		/**
+		 * An icon that displays arrow to indicate a dropdown button.
+		 *
+		 * @readonly
+		 * @member {module:ui/icon/iconview~IconView}
+		 */
+		this.arrowView = this._createArrowView();
+
+		// The DropdownButton interface expects the open event upon which will open the dropdown.
+		this.delegate( 'execute' ).to( this, 'open' );
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	render() {
+		super.render();
+
+		this.children.add( this.arrowView );
+	}
+
+	/**
+	 * Creates a {@link module:ui/icon/iconview~IconView} instance as {@link #arrowView}.
+	 *
+	 * @private
+	 * @returns {module:ui/icon/iconview~IconView}
+	 */
+	_createArrowView() {
+		const arrowView = new IconView();
+
+		arrowView.content = dropdownArrowIcon;
+
+		arrowView.extendTemplate( {
+			attributes: {
+				class: 'ck-dropdown__arrow'
+			}
+		} );
+
+		return arrowView;
+	}
+}

+ 219 - 0
packages/ckeditor5-ui/src/dropdown/button/splitbuttonview.js

@@ -0,0 +1,219 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module ui/dropdown/button/splitbuttonview
+ */
+
+import View from '../../view';
+import ButtonView from '../../button/buttonview';
+
+import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
+import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
+
+import dropdownArrowIcon from '../../../theme/icons/dropdown-arrow.svg';
+
+import '../../../theme/components/dropdown/splitbutton.css';
+
+/**
+ * The split button view class.
+ *
+ *		const view = new SplitButtonView();
+ *
+ *		view.set( {
+ *			label: 'A button',
+ *			keystroke: 'Ctrl+B',
+ *			tooltip: true
+ *		} );
+ *
+ *		view.render();
+ *
+ *		document.body.append( view.element );
+ *
+ * Also see the {@link module:ui/dropdown/utils~createDropdown `createDropdown()` util}.
+ *
+ * @implements module:ui/dropdown/button/dropdownbutton~DropdownButton
+ * @extends module:ui/view~View
+ */
+export default class SplitButtonView extends View {
+	/**
+	 * @inheritDoc
+	 */
+	constructor( locale ) {
+		super( locale );
+
+		const bind = this.bindTemplate;
+
+		// Implement the Button interface.
+		this.set( 'icon' );
+		this.set( 'isEnabled', true );
+		this.set( 'isOn', false );
+		this.set( 'isVisible', true );
+		this.set( 'keystroke' );
+		this.set( 'label' );
+		this.set( 'tabindex', -1 );
+		this.set( 'tooltip' );
+		this.set( 'tooltipPosition', 's' );
+		this.set( 'type', 'button' );
+		this.set( 'withText', false );
+
+		/**
+		 * Collection of the child views inside of the split button {@link #element}.
+		 *
+		 * @readonly
+		 * @member {module:ui/viewcollection~ViewCollection}
+		 */
+		this.children = this.createCollection();
+
+		/**
+		 * A main button of split button.
+		 *
+		 * @readonly
+		 * @member {module:ui/button/buttonview~ButtonView}
+		 */
+		this.actionView = this._createActionView();
+
+		/**
+		 * A secondary button of split button that opens dropdown.
+		 *
+		 * @readonly
+		 * @member {module:ui/button/buttonview~ButtonView}
+		 */
+		this.arrowView = this._createArrowView();
+
+		/**
+		 * Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. It manages
+		 * keystrokes of the split button:
+		 *
+		 * * <kbd>▶</kbd> moves focus to arrow view when action view is focused,
+		 * * <kbd>◀</kbd> moves focus to action view when arrow view is focused.
+		 *
+		 * @readonly
+		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
+		 */
+		this.keystrokes = new KeystrokeHandler();
+
+		/**
+		 * Tracks information about DOM focus in the dropdown.
+		 *
+		 * @readonly
+		 * @member {module:utils/focustracker~FocusTracker}
+		 */
+		this.focusTracker = new FocusTracker();
+
+		this.setTemplate( {
+			tag: 'div',
+
+			attributes: {
+				class: [
+					'ck-splitbutton',
+					bind.if( 'isVisible', 'ck-hidden', value => !value ),
+					this.arrowView.bindTemplate.if( 'isOn', 'ck-splitbutton_open' )
+				]
+			},
+
+			children: this.children
+		} );
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	render() {
+		super.render();
+
+		this.children.add( this.actionView );
+		this.children.add( this.arrowView );
+
+		this.focusTracker.add( this.actionView.element );
+		this.focusTracker.add( this.arrowView.element );
+
+		this.keystrokes.listenTo( this.element );
+
+		// Overrides toolbar focus cycling behavior.
+		this.keystrokes.set( 'arrowright', ( evt, cancel ) => {
+			if ( this.focusTracker.focusedElement === this.actionView.element ) {
+				this.arrowView.focus();
+
+				cancel();
+			}
+		} );
+
+		// Overrides toolbar focus cycling behavior.
+		this.keystrokes.set( 'arrowleft', ( evt, cancel ) => {
+			if ( this.focusTracker.focusedElement === this.arrowView.element ) {
+				this.actionView.focus();
+
+				cancel();
+			}
+		} );
+	}
+
+	/**
+	 * Focuses the {@link #actionView#element} of the action part of split button.
+	 */
+	focus() {
+		this.actionView.focus();
+	}
+
+	/**
+	 * Creates a {@link module:ui/button/buttonview~ButtonView} instance as {@link #actionView} and binds it with main split button
+	 * attributes.
+	 *
+	 * @private
+	 * @returns {module:ui/button/buttonview~ButtonView}
+	 */
+	_createActionView() {
+		const actionView = new ButtonView();
+
+		actionView.bind(
+			'icon',
+			'isEnabled',
+			'isOn',
+			'keystroke',
+			'label',
+			'tabindex',
+			'tooltip',
+			'tooltipPosition',
+			'type',
+			'withText'
+		).to( this );
+
+		actionView.extendTemplate( {
+			attributes: {
+				class: 'ck-splitbutton__action'
+			}
+		} );
+
+		actionView.delegate( 'execute' ).to( this );
+
+		return actionView;
+	}
+
+	/**
+	 * Creates a {@link module:ui/button/buttonview~ButtonView} instance as {@link #arrowView} and binds it with main split button
+	 * attributes.
+	 *
+	 * @private
+	 * @returns {module:ui/button/buttonview~ButtonView}
+	 */
+	_createArrowView() {
+		const arrowView = new ButtonView();
+
+		arrowView.icon = dropdownArrowIcon;
+
+		arrowView.extendTemplate( {
+			attributes: {
+				class: 'ck-splitbutton__arrow'
+			}
+		} );
+
+		arrowView.bind( 'isEnabled' ).to( this );
+
+		arrowView.delegate( 'execute' ).to( this, 'open' );
+
+		return arrowView;
+	}
+}

+ 0 - 49
packages/ckeditor5-ui/src/dropdown/createdropdown.js

@@ -1,49 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module ui/dropdown/createdropdown
- */
-
-import ButtonView from '../button/buttonview';
-import DropdownView from './dropdownview';
-import DropdownPanelView from './dropdownpanelview';
-
-/**
- * A helper which creates an instance of {@link module:ui/dropdown/dropdownview~DropdownView} class using
- * a provided {@link module:ui/dropdown/dropdownmodel~DropdownModel}.
- *
- *		const model = new Model( {
- *			label: 'A dropdown',
- *			isEnabled: true,
- *			isOn: false,
- *			withText: true
- *		} );
- *
- *		const dropdown = createDropdown( model );
- *
- *		dropdown.render();
- *
- *		// Will render a dropdown labeled "A dropdown" with an empty panel.
- *		document.body.appendChild( dropdown.element );
- *
- * The model instance remains in control of the dropdown after it has been created. E.g. changes to the
- * {@link module:ui/dropdown/dropdownmodel~DropdownModel#label `model.label`} will be reflected in the
- * dropdown button's {@link module:ui/button/buttonview~ButtonView#label} attribute and in DOM.
- *
- * Also see {@link module:ui/dropdown/list/createlistdropdown~createListDropdown}.
- *
- * @param {module:ui/dropdown/dropdownmodel~DropdownModel} model Model of this dropdown.
- * @param {module:utils/locale~Locale} locale The locale instance.
- * @returns {module:ui/dropdown/dropdownview~DropdownView} The dropdown view instance.
- */
-export default function createDropdown( model, locale ) {
-	const buttonView = new ButtonView( locale );
-	buttonView.bind( 'label', 'isOn', 'isEnabled', 'withText', 'keystroke', 'tooltip' ).to( model );
-
-	const panelView = new DropdownPanelView( locale );
-
-	return new DropdownView( locale, buttonView, panelView );
-}

+ 0 - 51
packages/ckeditor5-ui/src/dropdown/dropdownmodel.jsdoc

@@ -1,51 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module ui/dropdown/dropdownmodel
- */
-
-/**
- * The basic dropdown model interface.
- *
- * @interface module:ui/dropdown/dropdownmodel~DropdownModel
- */
-
-/**
- * The label of the dropdown.
- *
- * Also see {@link module:ui/button/buttonview~ButtonView#label}.
- *
- * @observable
- * @member {String} #label
- */
-
-/**
- * Controls whether the dropdown is enabled, i.e. it opens the panel when clicked.
- *
- * Also see {@link module:ui/button/buttonview~ButtonView#isEnabled}.
- *
- * @observable
- * @member {Boolean} #isEnabled
- */
-
-/**
- * Controls whether the dropdown is "on". It makes sense when a feature it represents
- * is currently active.
- *
- * Also see {@link module:ui/button/buttonview~ButtonView#isOn}.
- *
- * @observable
- * @member {Boolean} #isOn
- */
-
-/**
- * (Optional) Controls whether the label of the dropdown is visible.
- *
- * Also see {@link module:ui/button/buttonview~ButtonView#withText}.
- *
- * @observable
- * @member {Boolean} #withText
- */

+ 27 - 0
packages/ckeditor5-ui/src/dropdown/dropdownpanelfocusable.jsdoc

@@ -0,0 +1,27 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module ui/dropdown/dropdownpanelfocusable
+ */
+
+/**
+ * The dropdown panel interface for focusable contents. It provides two methods for managing focus of the contents
+ * of dropdown's panel.
+ *
+ * @interface module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable
+ */
+
+/**
+ * Focuses the view element or first item in view collection on opening dropdown's panel.
+ *
+ * @method #focus
+ */
+
+/**
+ * Focuses the view element or last item in view collection on opening dropdown's panel.
+ *
+ * @method #focusLast
+ */

+ 32 - 4
packages/ckeditor5-ui/src/dropdown/dropdownpanelview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -36,9 +36,9 @@ export default class DropdownPanelView extends View {
 		/**
 		 * Collection of the child views in this panel.
 		 *
-		 * A common child type is the {@link module:list/list~List}. See
-		 * {@link module:ui/dropdown/list/createlistdropdown~createListDropdown} to learn more
-		 * about list dropdowns.
+		 * A common child type is the {@link module:ui/list/listview~ListView} and {@link module:ui/toolbar/toolbarview~ToolbarView}.
+		 * See {@link module:ui/dropdown/utils~addListToDropdown} and
+		 * {@link module:ui/dropdown/utils~addToolbarToDropdown} to learn more about child views of dropdowns.
 		 *
 		 * @readonly
 		 * @member {module:ui/viewcollection~ViewCollection}
@@ -65,4 +65,32 @@ export default class DropdownPanelView extends View {
 			}
 		} );
 	}
+
+	/**
+	 * Focuses the view element or first item in view collection on opening dropdown's panel.
+	 *
+	 * See also {@link module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable}.
+	 */
+	focus() {
+		if ( this.children.length ) {
+			this.children.first.focus();
+		}
+	}
+
+	/**
+	 * Focuses the view element or last item in view collection on opening dropdown's panel.
+	 *
+	 * See also {@link module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable}.
+	 */
+	focusLast() {
+		if ( this.children.length ) {
+			const lastChild = this.children.last;
+
+			if ( typeof lastChild.focusLast === 'function' ) {
+				lastChild.focusLast();
+			} else {
+				lastChild.focus();
+			}
+		}
+	}
 }

+ 100 - 21
packages/ckeditor5-ui/src/dropdown/dropdownview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -11,14 +11,39 @@ import View from '../view';
 import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
 import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
 
+import '../../theme/components/dropdown/dropdown.css';
+
 /**
- * The dropdown view class.
+ * The dropdown view class. It manages the dropdown button and dropdown panel.
+ *
+ * In most cases, the easiest way to create a dropdown is by using the {@link module:ui/dropdown/utils~createDropdown}
+ * util:
+ *
+ *		const dropdown = createDropdown( locale );
+ *
+ *		// Configure dropdown's button properties:
+ *		dropdown.buttonView.set( {
+ *			label: 'A dropdown',
+ *			withText: true
+ *		} );
+ *
+ *		dropdown.render();
+ *
+ *		dropdown.panelView.element.textContent = 'Content of the panel';
+ *
+ *		// Will render a dropdown with a panel containing a "Content of the panel" text.
+ *		document.body.appendChild( dropdown.element );
+ *
+ * If you want to add a richer content to the dropdown panel, you can use the {@link module:ui/dropdown/utils~addListToDropdown}
+ * and {@link module:ui/dropdown/utils~addToolbarToDropdown} helpers. See more examples in
+ * {@link module:ui/dropdown/utils~createDropdown} documentation.
+ *
+ * If you want to create a completely custom dropdown, then you can compose it manually:
  *
- *		const button = new ButtonView( locale );
+ *		const button = new DropdownButtonView( locale );
  *		const panel = new DropdownPanelView( locale );
  *		const dropdown = new DropdownView( locale, button, panel );
  *
- *		panel.element.textContent = 'Content of the panel';
  *		button.set( {
  *			label: 'A dropdown',
  *			withText: true
@@ -26,32 +51,32 @@ import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
  *
  *		dropdown.render();
  *
+ *		panel.element.textContent = 'Content of the panel';
+ *
  *		// Will render a dropdown with a panel containing a "Content of the panel" text.
  *		document.body.appendChild( dropdown.element );
  *
- * Also see {@link module:ui/dropdown/createdropdown~createDropdown} and
- * {@link module:ui/dropdown/list/createlistdropdown~createListDropdown} to learn about different
- * dropdown creation helpers.
+ * However, dropdown created this way will contain little behavior. You will need to implement handlers for actions
+ * such as {@link module:ui/bindings/clickoutsidehandler~clickOutsideHandler clicking outside an open dropdown}
+ * (which should close it) and support for arrow keys inside the panel. Therefore, unless you really know what
+ * you do and you really need to do it, it is recommended to use the {@link module:ui/dropdown/utils~createDropdown} helper.
  *
  * @extends module:ui/view~View
  */
 export default class DropdownView extends View {
 	/**
-	 * @inheritDoc
+	 * Creates an instance of the dropdown.
+	 *
+	 * Also see {@link #render}.
+	 *
+	 * @param {module:utils/locale~Locale} [locale] The localization services instance.
+	 * @param {module:ui/dropdown/button/dropdownbutton~DropdownButton} buttonView
+	 * @param {module:ui/dropdown/dropdownpanelview~DropdownPanelView} panelView
 	 */
 	constructor( locale, buttonView, panelView ) {
 		super( locale );
 
-		// Extend button's template before it's registered as a child of the dropdown because
-		// by doing so, its #element is rendered and any post–render template extension will
-		// not be reflected in DOM.
-		buttonView.extendTemplate( {
-			attributes: {
-				class: [
-					'ck-dropdown__button'
-				]
-			}
-		} );
+		const bind = this.bindTemplate;
 
 		/**
 		 * Button of the dropdown view. Clicking the button opens the {@link #panelView}.
@@ -85,6 +110,16 @@ export default class DropdownView extends View {
 		 */
 		this.set( 'isOpen', false );
 
+		/**
+		 * Controls whether the dropdown is enabled, i.e. it can be clicked and execute an action.
+		 *
+		 * See {@link module:ui/button/buttonview~ButtonView#isEnabled}.
+		 *
+		 * @observable
+		 * @member {Boolean} #isEnabled
+		 */
+		this.set( 'isEnabled', true );
+
 		/**
 		 * Tracks information about DOM focus in the dropdown.
 		 *
@@ -110,7 +145,8 @@ export default class DropdownView extends View {
 
 			attributes: {
 				class: [
-					'ck-dropdown'
+					'ck-dropdown',
+					bind.to( 'isEnabled', isEnabled => isEnabled ? '' : 'ck-disabled' )
 				]
 			},
 
@@ -119,6 +155,49 @@ export default class DropdownView extends View {
 				panelView
 			]
 		} );
+
+		buttonView.extendTemplate( {
+			attributes: {
+				class: [
+					'ck-dropdown__button',
+				]
+			}
+		} );
+
+		/**
+		 * A child {@link module:ui/list/listview~ListView list view} of the dropdown located
+		 * in its {@link module:ui/dropdown/dropdownview~DropdownView#panelView panel}.
+		 *
+		 * **Note**: Only supported when dropdown has list view added using {@link module:ui/dropdown/utils~addListToDropdown}.
+		 *
+		 * @readonly
+		 * @member {module:ui/list/listview~ListView} #listView
+		 */
+
+		/**
+		 * A child toolbar of the dropdown located in the
+		 * {@link module:ui/dropdown/dropdownview~DropdownView#panelView panel}.
+		 *
+		 * **Note**: Only supported when dropdown has list view added using {@link module:ui/dropdown/utils~addToolbarToDropdown}.
+		 *
+		 * @readonly
+		 * @member {module:ui/toolbar/toolbarview~ToolbarView} #toolbarView
+		 */
+
+		/**
+		 * Fired when the toolbar button or list item is executed.
+		 *
+		 * For {@link #listView} It fires when one of the list items has been
+		 * {@link module:ui/list/listitemview~ListItemView#event:execute executed}.
+		 *
+		 * For {@link #toolbarView} It fires when one of the buttons has been
+		 * {@link module:ui/button/buttonview~ButtonView#event:execute executed}.
+		 *
+		 * **Note**: Only supported when dropdown has list view added using {@link module:ui/dropdown/utils~addListToDropdown}
+		 * or {@link module:ui/dropdown/utils~addToolbarToDropdown}.
+		 *
+		 * @event execute
+		 */
 	}
 
 	/**
@@ -127,8 +206,8 @@ export default class DropdownView extends View {
 	render() {
 		super.render();
 
-		// Toggle the the dropdown when it's button has been clicked.
-		this.listenTo( this.buttonView, 'execute', () => {
+		// Toggle the dropdown when its button has been clicked.
+		this.listenTo( this.buttonView, 'open', () => {
 			this.isOpen = !this.isOpen;
 		} );
 

+ 0 - 120
packages/ckeditor5-ui/src/dropdown/list/createlistdropdown.js

@@ -1,120 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module ui/dropdown/list/createlistdropdown
- */
-
-/* global document */
-
-import ListView from '../../list/listview';
-import ListItemView from '../../list/listitemview';
-import createDropdown from '../createdropdown';
-
-/**
- * Creates an instance of {@link module:ui/dropdown/list/listdropdownview~ListDropdownView} class using
- * a provided {@link module:ui/dropdown/list/listdropdownmodel~ListDropdownModel}.
- *
- *		const items = new Collection();
- *
- *		items.add( new Model( { label: 'First item', style: 'color: red' } ) );
- *		items.add( new Model( { label: 'Second item', style: 'color: green', class: 'foo' } ) );
- *
- *		const model = new Model( {
- *			isEnabled: true,
- *			items,
- *			isOn: false,
- *			label: 'A dropdown'
- *		} );
- *
- *		const dropdown = createListDropdown( model, locale );
- *
- *		// Will render a dropdown labeled "A dropdown" with a list in the panel
- *		// containing two items.
- *		dropdown.render()
- *		document.body.appendChild( dropdown.element );
- *
- * The model instance remains in control of the dropdown after it has been created. E.g. changes to the
- * {@link module:ui/dropdown/dropdownmodel~DropdownModel#label `model.label`} will be reflected in the
- * dropdown button's {@link module:ui/button/buttonview~ButtonView#label} attribute and in DOM.
- *
- * The
- * {@link module:ui/dropdown/list/listdropdownmodel~ListDropdownModel#items items collection}
- * of the {@link module:ui/dropdown/list/listdropdownmodel~ListDropdownModel model} also controls the
- * presence and attributes of respective {@link module:ui/list/listitemview~ListItemView list items}.
- *
- * See {@link module:ui/dropdown/createdropdown~createDropdown} and {@link module:list/list~List}.
- *
- * @param {module:ui/dropdown/list/listdropdownmodel~ListDropdownModel} model Model of the list dropdown.
- * @param {module:utils/locale~Locale} locale The locale instance.
- * @returns {module:ui/dropdown/list/listdropdownview~ListDropdownView} The list dropdown view instance.
- */
-export default function createListDropdown( model, locale ) {
-	const dropdownView = createDropdown( model, locale );
-	const listView = dropdownView.listView = new ListView( locale );
-
-	listView.items.bindTo( model.items ).using( itemModel => {
-		const item = new ListItemView( locale );
-
-		// Bind all attributes of the model to the item view.
-		item.bind( ...Object.keys( itemModel ) ).to( itemModel );
-
-		return item;
-	} );
-
-	// TODO: Delegate all events instead of just execute.
-	listView.items.delegate( 'execute' ).to( dropdownView );
-
-	dropdownView.panelView.children.add( listView );
-
-	dropdownView.on( 'change:isOpen', ( evt, name, value ) => {
-		if ( value ) {
-			attachDocumentClickListener( dropdownView );
-		} else {
-			dropdownView.stopListening( document );
-		}
-	} );
-
-	// Close the dropdown when one of the list items has been executed.
-	dropdownView.on( 'execute', () => {
-		dropdownView.isOpen = false;
-	} );
-
-	// If the dropdown panel is already open, the arrow down key should
-	// focus the first element in list.
-	dropdownView.keystrokes.set( 'arrowdown', ( data, cancel ) => {
-		if ( dropdownView.isOpen ) {
-			listView.focus();
-			cancel();
-		}
-	} );
-
-	// If the dropdown panel is already open, the arrow up key should
-	// focus the last element in the list.
-	dropdownView.keystrokes.set( 'arrowup', ( data, cancel ) => {
-		if ( dropdownView.isOpen ) {
-			listView.focusLast();
-			cancel();
-		}
-	} );
-
-	return dropdownView;
-}
-
-// Attaches a "click" listener in DOM to check if any element outside
-// the dropdown has been clicked.
-//
-// @private
-// @param {module:ui/dropdown/listdropdownview~ListDropdownView} dropdownView
-function attachDocumentClickListener( dropdownView ) {
-	// TODO: It will probably be focus/blur-based rather than click. It should be bound
-	// to focusmanager of some sort.
-	dropdownView.listenTo( document, 'click', ( evtInfo, { target: domEvtTarget } ) => {
-		// Collapse the dropdown when the webpage outside of the component is clicked.
-		if ( dropdownView.element != domEvtTarget && !dropdownView.element.contains( domEvtTarget ) ) {
-			dropdownView.isOpen = false;
-		}
-	} );
-}

+ 0 - 33
packages/ckeditor5-ui/src/dropdown/list/listdropdownmodel.jsdoc

@@ -1,33 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module ui/dropdown/list/listdropdownmodel
- */
-
-/**
- * The list dropdown model interface.
- *
- * @implements module:ui/dropdown/dropdownmodel~DropdownModel
- * @interface module:ui/dropdown/list/listdropdownmodel~ListDropdownModel
- */
-
-/**
- * A {@link module:utils/collection~Collection} of {@link module:utils/observablemixin~Observable}
- * that the inner dropdown {@link module:ui/list/listview~ListView} children are created from.
- *
- * Usually, it is a collection of {@link module:ui/model~Model models}.
- *
- * @observable
- * @member {module:utils/collection~Collection.<module:utils/observablemixin~Observable>} #items
- */
-
-/**
- * Fired when the list dropdown is executed. It fires when one of the list items in
- * {@link #items the collection} has been
- * {@link module:ui/list/listitemview~ListItemView#event:execute executed}.
- *
- * @event #execute
- */

+ 0 - 26
packages/ckeditor5-ui/src/dropdown/list/listdropdownview.jsdoc

@@ -1,26 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module ui/dropdown/list/createlistdropdown
- */
-
-/**
- * The list dropdown view.
- *
- * See {@link module:ui/dropdown/list/createlistdropdown~createListDropdown}.
- *
- * @abstract
- * @class module:ui/dropdown/list/listdropdownview~ListDropdownView
- * @extends module:ui/dropdown/dropdownview~DropdownView
- */
-
-/**
- * A child {@link module:ui/list/listview~ListView list view} of the dropdown located
- * in its {@link module:ui/dropdown/dropdownview~DropdownView#panelView panel}.
- *
- * @readonly
- * @member {module:ui/list/listview~ListView} #listView
- */

+ 243 - 0
packages/ckeditor5-ui/src/dropdown/utils.js

@@ -0,0 +1,243 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module ui/dropdown/utils
+ */
+
+import DropdownPanelView from './dropdownpanelview';
+import DropdownView from './dropdownview';
+import DropdownButtonView from './button/dropdownbuttonview';
+import ToolbarView from '../toolbar/toolbarview';
+import ListView from '../list/listview';
+import ListItemView from '../list/listitemview';
+
+import clickOutsideHandler from '../bindings/clickoutsidehandler';
+
+import '../../theme/components/dropdown/toolbardropdown.css';
+import '../../theme/components/dropdown/listdropdown.css';
+
+/**
+ * A helper for creating dropdowns. It creates an instance of a {@link module:ui/dropdown/dropdownview~DropdownView dropdown},
+ * with a {@link module:ui/dropdown/button/dropdownbutton~DropdownButton button},
+ * {@link module:ui/dropdown/dropdownpanelview~DropdownPanelView panel} and all standard dropdown's behaviors.
+ *
+ * # Creating dropdowns
+ *
+ * By default, the default {@link module:ui/dropdown/button/dropdownbuttonview~DropdownButtonView} class is used as
+ * definition of the button:
+ *
+ *		const dropdown = createDropdown( model );
+ *
+ *		// Configure dropdown's button properties:
+ *		dropdown.buttonView.set( {
+ *			label: 'A dropdown',
+ *			withText: true
+ *		} );
+ *
+ *		dropdown.render();
+ *
+ *		// Will render a dropdown labeled "A dropdown" with an empty panel.
+ *		document.body.appendChild( dropdown.element );
+ *
+ * You can also provide other button views (they need to implement the
+ * {module:ui/dropdown/button/dropdownbutton~DropdownButton} interface). For instance, you can use
+ * {@link module:ui/dropdown/button/splitbuttonview~SplitButtonView} to create a dropdown with a split button.
+ *
+ *		const dropdown = createDropdown( model, SplitButtonView );
+ *
+ *		// Configure dropdown's button properties:
+ *		dropdown.buttonView.set( {
+ *			label: 'A dropdown',
+ *			withText: true
+ *		} );
+ *
+ *		dropdown.buttonView.on( 'execute', () => {
+ *			// Add the behavior of the "action part" of the split button.
+ *			// Split button consists of the "action part" and "arrow part".
+ *			// The arrow opens the dropdown while the action part can have some other behavior.
+ * 		} );
+ *
+ *		dropdown.render();
+ *
+ *		// Will render a dropdown labeled "A dropdown" with an empty panel.
+ *		document.body.appendChild( dropdown.element );
+ *
+ * # Adding content to the dropdown's panel
+ *
+ * The content of the panel can be inserted directly into the `dropdown.panelView.element`:
+ *
+ *		dropdown.panelView.element.textContent = 'Content of the panel';
+ *
+ * However, most of the time you will want to add there either a {@link module:ui/list/listview~ListView list of options}
+ * or a list of buttons (i.e. a {@link module:ui/toolbar/toolbarview~ToolbarView toolbar}).
+ * To simplify the task, you can use, respectively, {@link module:ui/dropdown/utils~addListToDropdown} or
+ * {@link module:ui/dropdown/utils~addToolbarToDropdown} utils.
+ *
+ * @param {module:utils/locale~Locale} locale The locale instance.
+ * @param {Function} ButtonClass The dropdown button view class. Needs to implement the
+ * {@link module:ui/dropdown/button/dropdownbutton~DropdownButton} interface.
+ * @returns {module:ui/dropdown/dropdownview~DropdownView} The dropdown view instance.
+ */
+export function createDropdown( locale, ButtonClass = DropdownButtonView ) {
+	const buttonView = new ButtonClass( locale );
+
+	const panelView = new DropdownPanelView( locale );
+	const dropdownView = new DropdownView( locale, buttonView, panelView );
+
+	buttonView.bind( 'isEnabled' ).to( dropdownView );
+
+	if ( buttonView instanceof DropdownButtonView ) {
+		buttonView.bind( 'isOn' ).to( dropdownView, 'isOpen' );
+	} else {
+		buttonView.arrowView.bind( 'isOn' ).to( dropdownView, 'isOpen' );
+	}
+
+	addDefaultBehavior( dropdownView );
+
+	return dropdownView;
+}
+
+/**
+ * Adds an instance of {@link module:ui/toolbar/toolbarview~ToolbarView} to a dropdown.
+ *
+ *		const buttons = [];
+ *
+ *		// Either create a new ButtonView instance or create existing.
+ *		buttons.push( new ButtonView() );
+ *		buttons.push( editor.ui.componentFactory.get( 'someButton' ) );
+ *
+ *		const dropdown = createDropdown( locale );
+ *
+ *		addToolbarToDropdown( dropdown, buttons );
+ *
+ *		dropdown.toolbarView.isVertical = true;
+ *
+ *		// Will render a vertical button dropdown labeled "A button dropdown"
+ *		// with a button group in the panel containing two buttons.
+ *		dropdown.render()
+ *		document.body.appendChild( dropdown.element );
+ *
+ * See {@link module:ui/dropdown/utils~createDropdown} and {@link module:ui/toolbar/toolbarview~ToolbarView}.
+ *
+ * @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView A dropdown instance to which `ToolbarView` will be added.
+ * @param {Iterable.<module:ui/button/buttonview~ButtonView>} buttons
+ */
+export function addToolbarToDropdown( dropdownView, buttons ) {
+	const toolbarView = dropdownView.toolbarView = new ToolbarView();
+
+	dropdownView.extendTemplate( {
+		attributes: {
+			class: [ 'ck-toolbar-dropdown' ]
+		}
+	} );
+
+	buttons.map( view => toolbarView.items.add( view ) );
+
+	dropdownView.panelView.children.add( toolbarView );
+	toolbarView.items.delegate( 'execute' ).to( dropdownView );
+}
+
+/**
+ * Adds an instance of {@link module:ui/list/listview~ListView} to a dropdown.
+ *
+ *		const items = new Collection();
+ *
+ *		items.add( new Model( { label: 'First item', style: 'color: red' } ) );
+ *		items.add( new Model( { label: 'Second item', style: 'color: green', class: 'foo' } ) );
+ *
+ *		const dropdown = createDropdown( locale );
+ *
+ *		addListToDropdown( dropdown, items );
+ *
+ *		// Will render a dropdown with a list in the panel containing two items.
+ *		dropdown.render()
+ *		document.body.appendChild( dropdown.element );
+ *
+ * The `items` collection passed to this methods controls the presence and attributes of respective
+ * {@link module:ui/list/listitemview~ListItemView list items}.
+ *
+ *
+ * See {@link module:ui/dropdown/utils~createDropdown} and {@link module:list/list~List}.
+ *
+ * @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView A dropdown instance to which `ListVIew` will be added.
+ * @param {module:utils/collection~Collection} items
+ * that the inner dropdown {@link module:ui/list/listview~ListView} children are created from.
+ *
+ * Usually, it is a collection of {@link module:ui/model~Model models}.
+ */
+export function addListToDropdown( dropdownView, items ) {
+	const locale = dropdownView.locale;
+	const listView = dropdownView.listView = new ListView( locale );
+
+	listView.items.bindTo( items ).using( itemModel => {
+		const item = new ListItemView( locale );
+
+		// Bind all attributes of the model to the item view.
+		item.bind( ...Object.keys( itemModel ) ).to( itemModel );
+
+		return item;
+	} );
+
+	dropdownView.panelView.children.add( listView );
+
+	listView.items.delegate( 'execute' ).to( dropdownView );
+}
+
+// Add a set of default behaviors to dropdown view.
+//
+// @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
+function addDefaultBehavior( dropdownView ) {
+	closeDropdownOnBlur( dropdownView );
+	closeDropdownOnExecute( dropdownView );
+	focusDropdownContentsOnArrows( dropdownView );
+}
+
+// Adds a behavior to a dropdownView that closes opened dropdown when user clicks outside the dropdown.
+//
+// @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
+function closeDropdownOnBlur( dropdownView ) {
+	dropdownView.on( 'render', () => {
+		clickOutsideHandler( {
+			emitter: dropdownView,
+			activator: () => dropdownView.isOpen,
+			callback: () => {
+				dropdownView.isOpen = false;
+			},
+			contextElements: [ dropdownView.element ]
+		} );
+	} );
+}
+
+// Adds a behavior to a dropdownView that closes the dropdown view on "execute" event.
+//
+// @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
+function closeDropdownOnExecute( dropdownView ) {
+	// Close the dropdown when one of the list items has been executed.
+	dropdownView.on( 'execute', () => {
+		dropdownView.isOpen = false;
+	} );
+}
+
+// Adds a behavior to a dropdownView that focuses the dropdown's panel view contents on keystrokes.
+//
+// @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
+function focusDropdownContentsOnArrows( dropdownView ) {
+	// If the dropdown panel is already open, the arrow down key should focus the first child of the #panelView.
+	dropdownView.keystrokes.set( 'arrowdown', ( data, cancel ) => {
+		if ( dropdownView.isOpen ) {
+			dropdownView.panelView.focus();
+			cancel();
+		}
+	} );
+
+	// If the dropdown panel is already open, the arrow up key should focus the last child of the #panelView.
+	dropdownView.keystrokes.set( 'arrowup', ( data, cancel ) => {
+		if ( dropdownView.isOpen ) {
+			dropdownView.panelView.focusLast();
+			cancel();
+		}
+	} );
+}

+ 3 - 1
packages/ckeditor5-ui/src/editableui/editableuiview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -36,7 +36,9 @@ export default class EditableUIView extends View {
 			attributes: {
 				class: [
 					bind.to( 'isFocused', value => value ? 'ck-focused' : 'ck-blurred' ),
+					'ck',
 					'ck-editor__editable',
+					'ck-content',
 					'ck-rounded-corners'
 				],
 				contenteditable: bind.to( 'isReadOnly', value => !value ),

+ 1 - 1
packages/ckeditor5-ui/src/editableui/inline/inlineeditableuiview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 36 - 15
packages/ckeditor5-ui/src/editorui/boxed/boxededitoruiview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -8,6 +8,7 @@
  */
 
 import EditorUIView from '../../editorui/editoruiview';
+import LabelView from '../../label/labelview';
 import uid from '@ckeditor/ckeditor5-utils/src/uid';
 
 /**
@@ -25,7 +26,6 @@ export default class BoxedEditorUIView extends EditorUIView {
 	constructor( locale ) {
 		super( locale );
 
-		const t = this.t;
 		const ariaLabelUid = uid();
 
 		/**
@@ -46,6 +46,15 @@ export default class BoxedEditorUIView extends EditorUIView {
 		 */
 		this.main = this.createCollection();
 
+		/**
+		 * Voice label of the UI.
+		 *
+		 * @protected
+		 * @readonly
+		 * @member {module:ui/view~View} #_voiceLabelView
+		 */
+		this._voiceLabelView = this._createVoiceLabel( ariaLabelUid );
+
 		this.setTemplate( {
 			tag: 'div',
 
@@ -57,22 +66,12 @@ export default class BoxedEditorUIView extends EditorUIView {
 				],
 				role: 'application',
 				dir: 'ltr',
-				lang: locale.lang,
-				'aria-labelledby': `cke-editor__aria-label_${ ariaLabelUid }`
+				lang: locale.language,
+				'aria-labelledby': `ck-editor__aria-label_${ ariaLabelUid }`
 			},
 
 			children: [
-				{
-					tag: 'span',
-					attributes: {
-						id: `cke-editor__aria-label_${ ariaLabelUid }`,
-						class: 'cke-voice-label'
-					},
-					children: [
-						// TODO: Editor name?
-						t( 'Rich Text Editor' )
-					]
-				},
+				this._voiceLabelView,
 				{
 					tag: 'div',
 					attributes: {
@@ -92,4 +91,26 @@ export default class BoxedEditorUIView extends EditorUIView {
 			]
 		} );
 	}
+
+	/**
+	 * Creates a voice label view instance.
+	 *
+	 * @private
+	 * @returns {module:ui/label/labelview~LabelView}
+	 */
+	_createVoiceLabel( ariaLabelUid ) {
+		const t = this.t;
+		const voiceLabel = new LabelView();
+
+		voiceLabel.text = t( 'Rich Text Editor' );
+
+		voiceLabel.extendTemplate( {
+			attributes: {
+				id: `ck-editor__aria-label_${ ariaLabelUid }`,
+				class: 'ck-voice-label'
+			}
+		} );
+
+		return voiceLabel;
+	}
 }

+ 3 - 1
packages/ckeditor5-ui/src/editorui/editoruiview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -12,6 +12,8 @@
 import View from '../view';
 import Template from '../template';
 
+import '../../theme/components/editorui/editorui.css';
+
 /**
  * The editor UI view class. Base class for the editor main views.
  *

+ 1 - 1
packages/ckeditor5-ui/src/focuscycler.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 41 - 5
packages/ckeditor5-ui/src/icon/iconview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -11,6 +11,8 @@
 
 import View from '../view';
 
+import '../../theme/components/icon/icon.css';
+
 /**
  * The icon view class.
  *
@@ -43,6 +45,15 @@ export default class IconView extends View {
 		 */
 		this.set( 'viewBox', '0 0 20 20' );
 
+		/**
+		 * The fill color of the child `path.ck-icon__fill`.
+		 *
+		 * @observable
+		 * @default ''
+		 * @member {String} #fillColor
+		 */
+		this.set( 'fillColor', '' );
+
 		this.setTemplate( {
 			tag: 'svg',
 			ns: 'http://www.w3.org/2000/svg',
@@ -60,10 +71,18 @@ export default class IconView extends View {
 		super.render();
 
 		this._updateXMLContent();
+		this._colorFillPaths();
 
 		// This is a hack for lack of innerHTML binding.
 		// See: https://github.com/ckeditor/ckeditor5-ui/issues/99.
-		this.on( 'change:content', () => this._updateXMLContent() );
+		this.on( 'change:content', () => {
+			this._updateXMLContent();
+			this._colorFillPaths();
+		} );
+
+		this.on( 'change:fillColor', () => {
+			this._colorFillPaths();
+		} );
 	}
 
 	/**
@@ -73,9 +92,13 @@ export default class IconView extends View {
 	 */
 	_updateXMLContent() {
 		if ( this.content ) {
-			const svg = new DOMParser()
-				.parseFromString( this.content.trim(), 'image/svg+xml' )
-				.firstChild;
+			const parsed = new DOMParser().parseFromString( this.content.trim(), 'image/svg+xml' );
+			const svg = parsed.querySelector( 'svg' );
+			const viewBox = svg.getAttribute( 'viewBox' );
+
+			if ( viewBox ) {
+				this.viewBox = viewBox;
+			}
 
 			this.element.innerHTML = '';
 
@@ -84,4 +107,17 @@ export default class IconView extends View {
 			}
 		}
 	}
+
+	/**
+	 * Fills all child `path.ck-icon__fill` with the `#fillColor`.
+	 *
+	 * @private
+	 */
+	_colorFillPaths() {
+		if ( this.fillColor ) {
+			this.element.querySelectorAll( '.ck-icon__fill' ).forEach( path => {
+				path.style.fill = this.fillColor;
+			} );
+		}
+	}
 }

+ 1 - 1
packages/ckeditor5-ui/src/iframe/iframeview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 23 - 3
packages/ckeditor5-ui/src/inputtext/inputtextview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -9,6 +9,8 @@
 
 import View from '../view';
 
+import '../../theme/components/inputtext/inputtext.css';
+
 /**
  * The text input view class.
  *
@@ -65,12 +67,30 @@ export default class InputTextView extends View {
 				],
 				id: bind.to( 'id' ),
 				placeholder: bind.to( 'placeholder' ),
-				readonly: bind.to( 'isReadOnly' ),
-				value: bind.to( 'value' )
+				readonly: bind.to( 'isReadOnly' )
 			}
 		} );
 	}
 
+	/**
+	 * @inheritDoc
+	 */
+	render() {
+		super.render();
+
+		const setValue = value => {
+			this.element.value = ( !value && value !== 0 ) ? '' : value;
+		};
+
+		setValue( this.value );
+
+		// Bind `this.value` to the DOM element's value.
+		// We cannot use `value` DOM attribute because removing it on Edge does not clear the DOM element's value property.
+		this.on( 'change:value', ( evt, name, value ) => {
+			setValue( value );
+		} );
+	}
+
 	/**
 	 * Moves the focus to the input and selects the value.
 	 */

+ 3 - 1
packages/ckeditor5-ui/src/label/labelview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -9,6 +9,8 @@
 
 import View from '../view';
 
+import '../../theme/components/label/label.css';
+
 /**
  * The label view class.
  *

+ 2 - 1
packages/ckeditor5-ui/src/labeledinput/labeledinputview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -73,6 +73,7 @@ export default class LabeledInputView extends View {
 			tag: 'div',
 			attributes: {
 				class: [
+					'ck-labeled-input',
 					bind.if( 'isReadOnly', 'ck-disabled' )
 				]
 			},

+ 1 - 1
packages/ckeditor5-ui/src/list/listitemview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 4 - 1
packages/ckeditor5-ui/src/list/listview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -12,10 +12,13 @@ import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
 import FocusCycler from '../focuscycler';
 import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
 
+import '../../theme/components/list/list.css';
+
 /**
  * The list view class.
  *
  * @extends module:ui/view~View
+ * @implements module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable
  */
 export default class ListView extends View {
 	/**

+ 1 - 1
packages/ckeditor5-ui/src/model.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-ui/src/notification/notification.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 7 - 3
packages/ckeditor5-ui/src/panel/balloon/balloonpanelview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -14,6 +14,8 @@ import isElement from '@ckeditor/ckeditor5-utils/src/lib/lodash/isElement';
 import toUnit from '@ckeditor/ckeditor5-utils/src/dom/tounit';
 import global from '@ckeditor/ckeditor5-utils/src/dom/global';
 
+import '../../../theme/components/panel/balloonpanel.css';
+
 const toPx = toUnit( 'px' );
 const defaultLimiterElement = global.document.body;
 
@@ -220,8 +222,10 @@ export default class BalloonPanelView extends View {
 		const positionOptions = Object.assign( {}, {
 			element: this.element,
 			positions: [
+				defaultPositions.southArrowNorth,
 				defaultPositions.southArrowNorthWest,
 				defaultPositions.southArrowNorthEast,
+				defaultPositions.northArrowSouth,
 				defaultPositions.northArrowSouthWest,
 				defaultPositions.northArrowSouthEast
 			],
@@ -389,7 +393,7 @@ function getDomElement( object ) {
  * @default 30
  * @member {Number} module:ui/panel/balloon/balloonpanelview~BalloonPanelView.arrowHorizontalOffset
  */
-BalloonPanelView.arrowHorizontalOffset = 30;
+BalloonPanelView.arrowHorizontalOffset = 25;
 
 /**
  * A vertical offset of the arrow from the edge of the balloon. Controlled by CSS.
@@ -408,7 +412,7 @@ BalloonPanelView.arrowHorizontalOffset = 30;
  * @default 15
  * @member {Number} module:ui/panel/balloon/balloonpanelview~BalloonPanelView.arrowVerticalOffset
  */
-BalloonPanelView.arrowVerticalOffset = 15;
+BalloonPanelView.arrowVerticalOffset = 10;
 
 /**
  * A default set of positioning functions used by the balloon panel view

+ 3 - 2
packages/ckeditor5-ui/src/panel/balloon/contextualballoon.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -61,7 +61,8 @@ export default class ContextualBalloon extends Plugin {
 		 */
 		this.positionLimiter = () => {
 			const view = this.editor.editing.view;
-			const editableElement = view.selection.editableElement;
+			const viewDocument = view.document;
+			const editableElement = viewDocument.selection.editableElement;
 
 			if ( editableElement ) {
 				return view.domConverter.mapViewToDom( editableElement.root );

+ 3 - 1
packages/ckeditor5-ui/src/panel/sticky/stickypanelview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -12,6 +12,8 @@ import View from '../../view';
 import Template from '../../template';
 import toUnit from '@ckeditor/ckeditor5-utils/src/dom/tounit';
 
+import '../../../theme/components/panel/stickypanel.css';
+
 const toPx = toUnit( 'px' );
 
 /**

+ 5 - 5
packages/ckeditor5-ui/src/template.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -16,7 +16,7 @@ import View from './view';
 import ViewCollection from './viewcollection';
 import cloneDeepWith from '@ckeditor/ckeditor5-utils/src/lib/lodash/cloneDeepWith';
 import isObject from '@ckeditor/ckeditor5-utils/src/lib/lodash/isObject';
-import isDomNode from '@ckeditor/ckeditor5-utils/src/dom/isdomnode';
+import isNode from '@ckeditor/ckeditor5-utils/src/dom/isnode';
 import log from '@ckeditor/ckeditor5-utils/src/log';
 
 const xhtmlNs = 'http://www.w3.org/1999/xhtml';
@@ -237,7 +237,7 @@ export default class Template {
 	 *			console.log( view );
 	 *		}
 	 *
-	 * @returns {Iterator.<module:ui/view~View>}
+	 * @returns {Iterable.<module:ui/view~View>}
 	 */
 	* getViews() {
 		function* search( def ) {
@@ -703,7 +703,7 @@ export default class Template {
 
 					container.appendChild( child.element );
 				}
-			} else if ( isDomNode( child ) ) {
+			} else if ( isNode( child ) ) {
 				container.appendChild( child );
 			} else {
 				if ( isApplying ) {
@@ -1177,7 +1177,7 @@ function normalize( def ) {
 				children.push( def.children );
 			} else {
 				for ( const child of def.children ) {
-					if ( isTemplate( child ) || isView( child ) || isDomNode( child ) ) {
+					if ( isTemplate( child ) || isView( child ) || isNode( child ) ) {
 						children.push( child );
 					} else {
 						children.push( new Template( child ) );

+ 20 - 20
packages/ckeditor5-ui/src/toolbar/contextual/contextualtoolbar.js → packages/ckeditor5-ui/src/toolbar/balloon/balloontoolbar.js

@@ -1,10 +1,10 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
 /**
- * @module ui/toolbar/contextual/contextualtoolbar
+ * @module ui/toolbar/balloon/balloontoolbar
  */
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
@@ -22,12 +22,12 @@ import normalizeToolbarConfig from '../normalizetoolbarconfig';
  *
  * @extends module:core/plugin~Plugin
  */
-export default class ContextualToolbar extends Plugin {
+export default class BalloonToolbar extends Plugin {
 	/**
 	 * @inheritDoc
 	 */
 	static get pluginName() {
-		return 'ContextualToolbar';
+		return 'BalloonToolbar';
 	}
 
 	/**
@@ -53,7 +53,6 @@ export default class ContextualToolbar extends Plugin {
 		this.toolbarView.extendTemplate( {
 			attributes: {
 				class: [
-					'ck-editor-toolbar',
 					'ck-toolbar_floating'
 				]
 			}
@@ -84,7 +83,7 @@ export default class ContextualToolbar extends Plugin {
 		this._handleSelectionChange();
 		this._handleFocusChange();
 
-		// The appearance of the ContextualToolbar method is event–driven.
+		// The appearance of the BalloonToolbar method is event–driven.
 		// It is possible to stop the #show event and this prevent the toolbar from showing up.
 		this.decorate( 'show' );
 	}
@@ -96,7 +95,7 @@ export default class ContextualToolbar extends Plugin {
 	 * @inheritDoc
 	 */
 	afterInit() {
-		const config = normalizeToolbarConfig( this.editor.config.get( 'contextualToolbar' ) );
+		const config = normalizeToolbarConfig( this.editor.config.get( 'balloonToolbar' ) );
 		const factory = this.editor.ui.componentFactory;
 
 		this.toolbarView.fillFromConfig( config.items, factory );
@@ -128,8 +127,8 @@ export default class ContextualToolbar extends Plugin {
 	 * @private
 	 */
 	_handleSelectionChange() {
-		const selection = this.editor.document.selection;
-		const editingView = this.editor.editing.view;
+		const selection = this.editor.model.document.selection;
+		const viewDocument = this.editor.editing.view.document;
 
 		this.listenTo( selection, 'change:range', ( evt, data ) => {
 			// When the selection is not changed by a collaboration and when is not collapsed.
@@ -145,7 +144,7 @@ export default class ContextualToolbar extends Plugin {
 		// Hide the toolbar when the selection stops changing.
 		this.listenTo( this, '_selectionChangeDebounced', () => {
 			// This implementation assumes that only non–collapsed selections gets the contextual toolbar.
-			if ( editingView.isFocused && !editingView.selection.isCollapsed ) {
+			if ( viewDocument.isFocused && !viewDocument.selection.isCollapsed ) {
 				this.show();
 			}
 		} );
@@ -168,7 +167,7 @@ export default class ContextualToolbar extends Plugin {
 			return;
 		}
 
-		// Update the toolbar position upon #render (e.g. external document changes)
+		// Update the toolbar position upon change (e.g. external document changes)
 		// while it's visible.
 		this.listenTo( this.editor.editing.view, 'render', () => {
 			this._balloon.updatePosition( this._getBalloonPositionData() );
@@ -178,7 +177,7 @@ export default class ContextualToolbar extends Plugin {
 		this._balloon.add( {
 			view: this.toolbarView,
 			position: this._getBalloonPositionData(),
-			balloonClassName: 'ck-toolbar-container ck-editor-toolbar-container'
+			balloonClassName: 'ck-toolbar-container'
 		} );
 	}
 
@@ -201,10 +200,11 @@ export default class ContextualToolbar extends Plugin {
 	 */
 	_getBalloonPositionData() {
 		const editor = this.editor;
-		const editingView = editor.editing.view;
+		const view = editor.editing.view;
+		const viewDocument = view.document;
 
 		// Get direction of the selection.
-		const isBackward = editingView.selection.isBackward;
+		const isBackward = viewDocument.selection.isBackward;
 
 		return {
 			// Because the target for BalloonPanelView is a Rect (not DOMRange), it's geometry will stay fixed
@@ -212,8 +212,8 @@ export default class ContextualToolbar extends Plugin {
 			// computed and hence, the target is defined as a function instead of a static value.
 			// https://github.com/ckeditor/ckeditor5-ui/issues/195
 			target: () => {
-				const range = editingView.selection.getFirstRange();
-				const rangeRects = Rect.getDomRangeRects( editingView.domConverter.viewRangeToDom( range ) );
+				const range = viewDocument.selection.getFirstRange();
+				const rangeRects = Rect.getDomRangeRects( view.domConverter.viewRangeToDom( range ) );
 
 				// Select the proper range rect depending on the direction of the selection.
 				if ( isBackward ) {
@@ -283,20 +283,20 @@ function getBalloonPositions( isBackward ) {
 }
 
 /**
- * Contextual toolbar configuration. Used by the {@link module:ui/toolbar/contextual/contextualtoolbar~ContextualToolbar}
+ * Contextual toolbar configuration. Used by the {@link module:ui/toolbar/balloon/balloontoolbar~BalloonToolbar}
  * feature.
  *
  *		const config = {
- *			contextualToolbar: [ 'bold', 'italic', 'undo', 'redo' ]
+ *			balloonToolbar: [ 'bold', 'italic', 'undo', 'redo' ]
  *		};
  *
  * You can also use `'|'` to create a separator between groups of items:
  *
  *		const config = {
- *			contextualToolbar: [ 'bold', 'italic', | 'undo', 'redo' ]
+ *			balloonToolbar: [ 'bold', 'italic', | 'undo', 'redo' ]
  *		};
  *
  * Read also about configuring the main editor toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
  *
- * @member {Array.<String>|Object} module:core/editor/editorconfig~EditorConfig#contextualToolbar
+ * @member {Array.<String>|Object} module:core/editor/editorconfig~EditorConfig#balloonToolbar
  */

+ 1 - 1
packages/ckeditor5-ui/src/toolbar/enabletoolbarkeyboardfocus.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 3 - 3
packages/ckeditor5-ui/src/toolbar/normalizetoolbarconfig.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -12,12 +12,12 @@
  *
  * * may be defined as an `Array`:
  *
- * 		toolbar: [ 'headings', 'bold', 'italic', 'link', ... ]
+ * 		toolbar: [ 'heading', 'bold', 'italic', 'link', ... ]
  *
  * * or an `Object`:
  *
  *		toolbar: {
- *			items: [ 'headings', 'bold', 'italic', 'link', ... ],
+ *			items: [ 'heading', 'bold', 'italic', 'link', ... ],
  *			...
  *		}
  *

+ 1 - 1
packages/ckeditor5-ui/src/toolbar/toolbarseparatorview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 32 - 2
packages/ckeditor5-ui/src/toolbar/toolbarview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -15,10 +15,13 @@ import ToolbarSeparatorView from './toolbarseparatorview';
 import preventDefault from '../bindings/preventdefault.js';
 import log from '@ckeditor/ckeditor5-utils/src/log';
 
+import '../../theme/components/toolbar/toolbar.css';
+
 /**
  * The toolbar view class.
  *
  * @extends module:ui/view~View
+ * @implements module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable
  */
 export default class ToolbarView extends View {
 	/**
@@ -27,6 +30,8 @@ export default class ToolbarView extends View {
 	constructor( locale ) {
 		super( locale );
 
+		const bind = this.bindTemplate;
+
 		/**
 		 * Collection of the toolbar items (like buttons).
 		 *
@@ -51,6 +56,22 @@ export default class ToolbarView extends View {
 		 */
 		this.keystrokes = new KeystrokeHandler();
 
+		/**
+		 * Controls the orientation of toolbar items.
+		 *
+		 * @observable
+		 * @member {Boolean} #isVertical
+		 */
+		this.set( 'isVertical', false );
+
+		/**
+		 * An additional CSS class added to the {@link #element}.
+		 *
+		 * @observable
+		 * @member {String} #className
+		 */
+		this.set( 'className' );
+
 		/**
 		 * Helps cycling over focusable {@link #items} in the toolbar.
 		 *
@@ -75,7 +96,9 @@ export default class ToolbarView extends View {
 			tag: 'div',
 			attributes: {
 				class: [
-					'ck-toolbar'
+					'ck-toolbar',
+					bind.if( 'isVertical', 'ck-toolbar_vertical' ),
+					bind.to( 'className' )
 				]
 			},
 
@@ -118,6 +141,13 @@ export default class ToolbarView extends View {
 		this._focusCycler.focusFirst();
 	}
 
+	/**
+	 * Focuses the last focusable in {@link #items}.
+	 */
+	focusLast() {
+		this._focusCycler.focusLast();
+	}
+
 	/**
 	 * A utility which expands a plain toolbar configuration into
 	 * {@link module:ui/toolbar/toolbarview~ToolbarView#items} using a given component factory.

+ 3 - 1
packages/ckeditor5-ui/src/tooltip/tooltipview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -9,6 +9,8 @@
 
 import View from '../view';
 
+import '../../theme/components/tooltip/tooltip.css';
+
 /**
  * The tooltip view class.
  *

+ 16 - 3
packages/ckeditor5-ui/src/view.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -16,6 +16,8 @@ import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
 import isIterable from '@ckeditor/ckeditor5-utils/src/isiterable';
 
+import '../theme/globals/globals.css';
+
 /**
  * The basic view class, which represents an HTML element created out of a
  * {@link module:ui/view~View#template}. Views are building blocks of the user interface and handle
@@ -109,7 +111,7 @@ export default class View {
 		 *
 		 *		const view = new SampleView();
 		 *
-		 *		// Renders the #template
+		 *		// Renders the #template.
 		 *		view.render();
 		 *
 		 *		// Append the HTML element of the view to <body>.
@@ -182,12 +184,14 @@ export default class View {
 		 */
 
 		/**
-		 * Cached {@link @link module:ui/template~BindChain bind chain} object created by the
+		 * Cached {@link module:ui/template~BindChain bind chain} object created by the
 		 * {@link #template}. See {@link #bindTemplate}.
 		 *
 		 * @private
 		 * @member {Object} #_bindTemplate
 		 */
+
+		this.decorate( 'render' );
 	}
 
 	/**
@@ -490,6 +494,15 @@ export default class View {
 
 		this._viewCollections.map( c => c.destroy() );
 	}
+
+	/**
+	 * Event fired by the {@link #render} method. Actual rendering is executed as a listener to
+	 * this event with the default priority.
+	 *
+	 * See {@link module:utils/observablemixin~ObservableMixin.decorate} for more information and samples.
+	 *
+	 * @event render
+	 */
 }
 
 mix( View, DomEmitterMixin );

+ 2 - 2
packages/ckeditor5-ui/src/viewcollection.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -53,7 +53,7 @@ export default class ViewCollection extends Collection {
 	/**
 	 * Creates a new instance of the {@link module:ui/viewcollection~ViewCollection}.
 	 *
-	 * @param {module:utils/locale~Locale} [locale] The {@link module:core/editor~Editor editor's locale} instance.
+	 * @param {module:utils/locale~Locale} [locale] The {@link module:core/editor/editor~Editor editor's locale} instance.
 	 */
 	constructor( locale ) {
 		super( {

+ 1 - 1
packages/ckeditor5-ui/tests/_utils-tests/utils.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-ui/tests/_utils/utils.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-ui/tests/bindings/clickoutsidehandler.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-ui/tests/bindings/preventdefault.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-ui/tests/bindings/submithandler.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 18 - 10
packages/ckeditor5-ui/tests/button/buttonview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -36,6 +36,10 @@ describe( 'ButtonView', () => {
 		it( 'creates #labelView', () => {
 			expect( view.labelView ).to.be.instanceOf( View );
 		} );
+
+		it( 'creates #iconView', () => {
+			expect( view.iconView ).to.be.instanceOf( IconView );
+		} );
 	} );
 
 	describe( '<button> bindings', () => {
@@ -99,7 +103,7 @@ describe( 'ButtonView', () => {
 
 			it( 'it reacts to #tooltipPosition attribute', () => {
 				view.tooltip = 'foo';
-				view.icon = 'bar';
+				view.icon = '<svg></svg>';
 
 				expect( view.tooltipPosition ).to.equal( 's' );
 				expect( view.tooltipView.position ).to.equal( 's' );
@@ -233,29 +237,33 @@ describe( 'ButtonView', () => {
 	} );
 
 	describe( 'icon', () => {
-		it( 'is not initially set', () => {
+		it( 'is omited in #children when view#icon is not defined', () => {
+			view = new ButtonView( locale );
+			view.render();
+
 			expect( view.element.childNodes ).to.have.length( 2 );
-			expect( view.iconView ).to.be.undefined;
+			expect( view.iconView.element ).to.be.null;
 		} );
 
-		it( 'is set when view#icon is defined', () => {
+		it( 'is added to the #children when view#icon is defined', () => {
 			view = new ButtonView( locale );
-			view.icon = 'foo';
+			view.icon = '<svg></svg>';
 			view.render();
 
 			expect( view.element.childNodes ).to.have.length( 3 );
 			expect( view.element.childNodes[ 0 ] ).to.equal( view.iconView.element );
 
 			expect( view.iconView ).to.instanceOf( IconView );
-			expect( view.iconView.content ).to.equal( 'foo' );
+			expect( view.iconView.content ).to.equal( '<svg></svg>' );
+			expect( view.iconView.element.classList.contains( 'ck-button__icon' ) ).to.be.true;
 
-			view.icon = 'bar';
-			expect( view.iconView.content ).to.equal( 'bar' );
+			view.icon = '<svg>bar</svg>';
+			expect( view.iconView.content ).to.equal( '<svg>bar</svg>' );
 		} );
 
 		it( 'is destroyed with the view', () => {
 			view = new ButtonView( locale );
-			view.icon = 'foo';
+			view.icon = '<svg></svg>';
 			view.render();
 
 			const spy = sinon.spy( view.iconView, 'destroy' );

+ 8 - 2
packages/ckeditor5-ui/tests/componentfactory.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -33,7 +33,7 @@ describe( 'ComponentFactory', () => {
 			factory.add( 'bar', () => {} );
 			factory.add( 'Baz', () => {} );
 
-			expect( Array.from( factory.names() ) ).to.have.members( [ 'foo', 'bar', 'baz' ] );
+			expect( Array.from( factory.names() ) ).to.have.members( [ 'foo', 'bar', 'Baz' ] );
 		} );
 	} );
 
@@ -53,6 +53,12 @@ describe( 'ComponentFactory', () => {
 				factory.add( 'foo', () => {} );
 			} ).to.throw( CKEditorError, /^componentfactory-item-exists/ );
 		} );
+
+		it( 'does not normalize component names', () => {
+			factory.add( 'FoO', () => {} );
+
+			expect( Array.from( factory.names() ) ).to.have.members( [ 'FoO' ] );
+		} );
 	} );
 
 	describe( 'create()', () => {

+ 47 - 0
packages/ckeditor5-ui/tests/dropdown/button/dropdownbuttonview.js

@@ -0,0 +1,47 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+import IconView from '../../../src/icon/iconview';
+import DropdownButtonView from '../../../src/dropdown/button/dropdownbuttonview';
+
+testUtils.createSinonSandbox();
+
+describe( 'DropdownButtonView', () => {
+	let locale, view;
+
+	beforeEach( () => {
+		locale = { t() {} };
+
+		view = new DropdownButtonView( locale );
+		view.render();
+	} );
+
+	describe( 'constructor()', () => {
+		it( 'sets view#locale', () => {
+			expect( view.locale ).to.equal( locale );
+		} );
+
+		it( 'creates view#arrowView', () => {
+			expect( view.arrowView ).to.be.instanceOf( IconView );
+		} );
+
+		it( 'creates element from template', () => {
+			expect( view.element.tagName ).to.equal( 'BUTTON' );
+		} );
+	} );
+
+	describe( 'bindings', () => {
+		it( 'delegates view#execute to view#open', () => {
+			const spy = sinon.spy();
+
+			view.on( 'open', spy );
+
+			view.fire( 'execute' );
+
+			sinon.assert.calledOnce( spy );
+		} );
+	} );
+} );

+ 234 - 0
packages/ckeditor5-ui/tests/dropdown/button/splitbuttonview.js

@@ -0,0 +1,234 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
+import ButtonView from '../../../src/button/buttonview';
+import SplitButtonView from '../../../src/dropdown/button/splitbuttonview';
+
+testUtils.createSinonSandbox();
+
+describe( 'SplitButtonView', () => {
+	let locale, view;
+
+	beforeEach( () => {
+		locale = { t() {} };
+
+		view = new SplitButtonView( locale );
+		view.render();
+	} );
+
+	describe( 'constructor()', () => {
+		it( 'sets view#locale', () => {
+			expect( view.locale ).to.equal( locale );
+		} );
+
+		it( 'creates view#actionView', () => {
+			expect( view.actionView ).to.be.instanceOf( ButtonView );
+			expect( view.actionView.element.classList.contains( 'ck-splitbutton__action' ) ).to.be.true;
+		} );
+
+		it( 'creates view#arrowView', () => {
+			expect( view.arrowView ).to.be.instanceOf( ButtonView );
+			expect( view.arrowView.element.classList.contains( 'ck-splitbutton__arrow' ) ).to.be.true;
+			expect( view.arrowView.icon ).to.be.not.undefined;
+		} );
+
+		it( 'creates element from template', () => {
+			expect( view.element.tagName ).to.equal( 'DIV' );
+			expect( view.element.classList.contains( 'ck-splitbutton' ) ).to.be.true;
+		} );
+
+		it( 'binds #isVisible to the template', () => {
+			expect( view.element.classList.contains( 'ck-hidden' ) ).to.be.false;
+
+			view.isVisible = false;
+
+			expect( view.element.classList.contains( 'ck-hidden' ) ).to.be.true;
+
+			// There should be no binding to the action view. Only the entire split button should react.
+			expect( view.actionView.element.classList.contains( 'ck-hidden' ) ).to.be.false;
+		} );
+
+		it( 'binds arrowView#isOn to the template', () => {
+			view.arrowView.isOn = true;
+			expect( view.element.classList.contains( 'ck-splitbutton_open' ) ).to.be.true;
+
+			view.arrowView.isOn = false;
+			expect( view.element.classList.contains( 'ck-splitbutton_open' ) ).to.be.false;
+		} );
+
+		describe( 'activates keyboard navigation for the toolbar', () => {
+			it( 'so "arrowright" on view#arrowView does nothing', () => {
+				const keyEvtData = {
+					keyCode: keyCodes.arrowright,
+					preventDefault: sinon.spy(),
+					stopPropagation: sinon.spy()
+				};
+
+				view.focusTracker.isFocused = true;
+				view.focusTracker.focusedElement = view.arrowView.element;
+
+				const spy = sinon.spy( view.actionView, 'focus' );
+
+				view.keystrokes.press( keyEvtData );
+				sinon.assert.notCalled( spy );
+				sinon.assert.notCalled( keyEvtData.preventDefault );
+				sinon.assert.notCalled( keyEvtData.stopPropagation );
+			} );
+
+			it( 'so "arrowleft" on view#arrowView focuses view#actionView', () => {
+				const keyEvtData = {
+					keyCode: keyCodes.arrowleft,
+					preventDefault: sinon.spy(),
+					stopPropagation: sinon.spy()
+				};
+
+				view.focusTracker.isFocused = true;
+				view.focusTracker.focusedElement = view.arrowView.element;
+
+				const spy = sinon.spy( view.actionView, 'focus' );
+
+				view.keystrokes.press( keyEvtData );
+				sinon.assert.calledOnce( spy );
+				sinon.assert.calledOnce( keyEvtData.preventDefault );
+				sinon.assert.calledOnce( keyEvtData.stopPropagation );
+			} );
+
+			it( 'so "arrowright" on view#actionView focuses view#arrowView', () => {
+				const keyEvtData = {
+					keyCode: keyCodes.arrowright,
+					preventDefault: sinon.spy(),
+					stopPropagation: sinon.spy()
+				};
+
+				view.focusTracker.isFocused = true;
+				view.focusTracker.focusedElement = view.actionView.element;
+
+				const spy = sinon.spy( view.arrowView, 'focus' );
+
+				view.keystrokes.press( keyEvtData );
+				sinon.assert.calledOnce( spy );
+				sinon.assert.calledOnce( keyEvtData.preventDefault );
+				sinon.assert.calledOnce( keyEvtData.stopPropagation );
+			} );
+
+			it( 'so "arrowleft" on view#actionsView does nothing', () => {
+				const keyEvtData = {
+					keyCode: keyCodes.arrowleft,
+					preventDefault: sinon.spy(),
+					stopPropagation: sinon.spy()
+				};
+
+				view.focusTracker.isFocused = true;
+				view.focusTracker.focusedElement = view.actionView.element;
+
+				const spy = sinon.spy( view.arrowView, 'focus' );
+
+				view.keystrokes.press( keyEvtData );
+				sinon.assert.notCalled( spy );
+				sinon.assert.notCalled( keyEvtData.preventDefault );
+				sinon.assert.notCalled( keyEvtData.stopPropagation );
+			} );
+		} );
+	} );
+
+	describe( 'bindings', () => {
+		it( 'delegates actionView#execute to view#execute', () => {
+			const spy = sinon.spy();
+
+			view.on( 'execute', spy );
+
+			view.actionView.fire( 'execute' );
+
+			sinon.assert.calledOnce( spy );
+		} );
+
+		it( 'binds actionView#icon to view', () => {
+			expect( view.actionView.icon ).to.be.undefined;
+
+			view.icon = 'foo';
+
+			expect( view.actionView.icon ).to.equal( 'foo' );
+		} );
+
+		it( 'binds actionView#isEnabled to view', () => {
+			expect( view.actionView.isEnabled ).to.be.true;
+
+			view.isEnabled = false;
+
+			expect( view.actionView.isEnabled ).to.be.false;
+		} );
+
+		it( 'binds actionView#label to view', () => {
+			expect( view.actionView.label ).to.be.undefined;
+
+			view.label = 'foo';
+
+			expect( view.actionView.label ).to.equal( 'foo' );
+		} );
+
+		it( 'delegates arrowView#execute to view#open', () => {
+			const spy = sinon.spy();
+
+			view.on( 'open', spy );
+
+			view.arrowView.fire( 'execute' );
+
+			sinon.assert.calledOnce( spy );
+		} );
+
+		it( 'binds arrowView#isEnabled to view', () => {
+			expect( view.arrowView.isEnabled ).to.be.true;
+
+			view.isEnabled = false;
+
+			expect( view.arrowView.isEnabled ).to.be.false;
+		} );
+
+		it( 'binds actionView#tabindex to view', () => {
+			expect( view.actionView.tabindex ).to.equal( -1 );
+
+			view.tabindex = 1;
+
+			expect( view.actionView.tabindex ).to.equal( 1 );
+		} );
+
+		// Makes little sense for split button but the Button interface specifies it, so let's support it.
+		it( 'binds actionView#type to view', () => {
+			expect( view.actionView.type ).to.equal( 'button' );
+
+			view.type = 'submit';
+
+			expect( view.actionView.type ).to.equal( 'submit' );
+		} );
+
+		it( 'binds actionView#withText to view', () => {
+			expect( view.actionView.withText ).to.equal( false );
+
+			view.withText = true;
+
+			expect( view.actionView.withText ).to.equal( true );
+		} );
+
+		it( 'binds actionView#tooltipPosition to view', () => {
+			expect( view.actionView.tooltipPosition ).to.equal( 's' );
+
+			view.tooltipPosition = 'n';
+
+			expect( view.actionView.tooltipPosition ).to.equal( 'n' );
+		} );
+	} );
+
+	describe( 'focus()', () => {
+		it( 'focuses the actionButton', () => {
+			const spy = sinon.spy( view.actionView, 'focus' );
+
+			view.focus();
+
+			sinon.assert.calledOnce( spy );
+		} );
+	} );
+} );

+ 0 - 64
packages/ckeditor5-ui/tests/dropdown/createdropdown.js

@@ -1,64 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-import utilsTestUtils from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
-import createDropdown from '../../src/dropdown/createdropdown';
-import Model from '../../src/model';
-import DropdownView from '../../src/dropdown/dropdownview';
-import DropdownPanelView from '../../src/dropdown/dropdownpanelview';
-import ButtonView from '../../src/button/buttonview';
-
-const assertBinding = utilsTestUtils.assertBinding;
-
-describe( 'createDropdown', () => {
-	it( 'accepts model', () => {
-		const modelDef = {
-			label: 'foo',
-			isOn: false,
-			isEnabled: true,
-			withText: false,
-			tooltip: false
-		};
-
-		const model = new Model( modelDef );
-		const view = createDropdown( model );
-
-		assertBinding( view.buttonView,
-			modelDef,
-			[
-				[ model, { label: 'bar', isOn: true, isEnabled: false, withText: true, tooltip: true } ]
-			],
-			{ label: 'bar', isOn: true, isEnabled: false, withText: true, tooltip: true }
-		);
-	} );
-
-	it( 'accepts locale', () => {
-		const locale = {};
-		const view = createDropdown( new Model(), locale );
-
-		expect( view.locale ).to.equal( locale );
-		expect( view.buttonView.locale ).to.equal( locale );
-		expect( view.panelView.locale ).to.equal( locale );
-	} );
-
-	it( 'returns view', () => {
-		const view = createDropdown( new Model() );
-
-		expect( view ).to.be.instanceOf( DropdownView );
-	} );
-
-	it( 'creates dropdown#buttonView out of ButtonView', () => {
-		const view = createDropdown( new Model() );
-
-		expect( view.buttonView ).to.be.instanceOf( ButtonView );
-	} );
-
-	it( 'creates dropdown#panelView out of DropdownPanelView', () => {
-		const view = createDropdown( new Model() );
-
-		expect( view.panelView ).to.be.instanceOf( DropdownPanelView );
-	} );
-} );
-

+ 54 - 1
packages/ckeditor5-ui/tests/dropdown/dropdownpanelview.js

@@ -1,5 +1,6 @@
+
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -7,6 +8,7 @@
 
 import ViewCollection from '../../src/viewcollection';
 import DropdownPanelView from '../../src/dropdown/dropdownpanelview';
+import View from '../../src/view';
 
 describe( 'DropdownPanelView', () => {
 	let view, locale;
@@ -63,4 +65,55 @@ describe( 'DropdownPanelView', () => {
 			} );
 		} );
 	} );
+
+	describe( 'focus()', () => {
+		it( 'does nothing for empty panel', () => {
+			expect( () => view.focus() ).to.not.throw();
+		} );
+
+		it( 'focuses first child view', () => {
+			const firstChildView = new View();
+
+			firstChildView.focus = sinon.spy();
+
+			view.children.add( firstChildView );
+			view.children.add( new View() );
+
+			view.focus();
+
+			sinon.assert.calledOnce( firstChildView.focus );
+		} );
+	} );
+
+	describe( 'focusLast()', () => {
+		it( 'does nothing for empty panel', () => {
+			expect( () => view.focusLast() ).to.not.throw();
+		} );
+
+		it( 'focuses last child view', () => {
+			const lastChildView = new View();
+
+			lastChildView.focusLast = sinon.spy();
+
+			view.children.add( new View() );
+			view.children.add( lastChildView );
+
+			view.focusLast();
+
+			sinon.assert.calledOnce( lastChildView.focusLast );
+		} );
+
+		it( 'focuses last child view even if it does not have focusLast() method', () => {
+			const lastChildView = new View();
+
+			lastChildView.focus = sinon.spy();
+
+			view.children.add( new View() );
+			view.children.add( lastChildView );
+
+			view.focusLast();
+
+			sinon.assert.calledOnce( lastChildView.focus );
+		} );
+	} );
 } );

Некоторые файлы не были показаны из-за большого количества измененных файлов