imagetoolbar.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module image/imagetoolbar
  7. */
  8. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  9. import ToolbarView from '@ckeditor/ckeditor5-ui/src/toolbar/toolbarview';
  10. import BalloonPanelView from '@ckeditor/ckeditor5-ui/src/balloonpanel/balloonpanelview';
  11. import Template from '@ckeditor/ckeditor5-ui/src/template';
  12. import { isImageWidget } from './utils';
  13. import throttle from '@ckeditor/ckeditor5-utils/src/lib/lodash/throttle';
  14. import global from '@ckeditor/ckeditor5-utils/src/dom/global';
  15. const arrowVOffset = BalloonPanelView.arrowVerticalOffset;
  16. const positions = {
  17. // [text range]
  18. // ^
  19. // +-----------------+
  20. // | Balloon |
  21. // +-----------------+
  22. south: ( targetRect, balloonRect ) => ( {
  23. top: targetRect.bottom + arrowVOffset,
  24. left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
  25. name: 's'
  26. } ),
  27. // +-----------------+
  28. // | Balloon |
  29. // +-----------------+
  30. // V
  31. // [text range]
  32. north: ( targetRect, balloonRect ) => ( {
  33. top: targetRect.top - balloonRect.height - arrowVOffset,
  34. left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
  35. name: 'n'
  36. } )
  37. };
  38. /**
  39. * Image toolbar class. Creates image toolbar placed inside balloon panel that is showed when image widget is selected.
  40. * Toolbar components are created using editor's {@link module:ui/componentfactory~ComponentFactory ComponentFactory}
  41. * based on {@link module:core/editor/editor~Editor#config configuration} stored under `image.toolbar`.
  42. * Other plugins can add new components to the default toolbar configuration by pushing them to `image.defaultToolbar`
  43. * configuration. Default configuration is used when `image.toolbar` config is not present.
  44. *
  45. * @extends module:core/plugin~Plugin.
  46. */
  47. export default class ImageToolbar extends Plugin {
  48. /**
  49. * @inheritDoc
  50. */
  51. constructor( editor ) {
  52. super( editor );
  53. editor.config.set( 'image.defaultToolbar', [] );
  54. }
  55. /**
  56. * @inheritDoc
  57. */
  58. afterInit() {
  59. const editor = this.editor;
  60. const toolbarConfig = editor.config.get( 'image.toolbar' ) || editor.config.get( 'image.defaultToolbar' );
  61. // Don't add the toolbar if there is no configuration.
  62. if ( !toolbarConfig.length ) {
  63. return;
  64. }
  65. // Create a plain toolbar instance.
  66. const toolbar = new ToolbarView();
  67. // Create a BalloonPanelView instance.
  68. const panel = new BalloonPanelView( editor.locale );
  69. Template.extend( panel.template, {
  70. attributes: {
  71. class: [
  72. 'ck-toolbar__container'
  73. ]
  74. }
  75. } );
  76. // Putting the toolbar inside of the balloon panel.
  77. panel.content.add( toolbar );
  78. return editor.ui.view.body.add( panel ).then( () => {
  79. const editingView = editor.editing.view;
  80. const promises = [];
  81. for ( let name of toolbarConfig ) {
  82. promises.push( toolbar.items.add( editor.ui.componentFactory.create( name ) ) );
  83. }
  84. // Let the focusTracker know about new focusable UI element.
  85. editor.ui.focusTracker.add( panel.element );
  86. // Hide the panel when editor loses focus but no the other way around.
  87. panel.listenTo( editor.ui.focusTracker, 'change:isFocused', ( evt, name, is, was ) => {
  88. if ( was && !is ) {
  89. panel.hide();
  90. }
  91. } );
  92. const attachToolbarCallback = throttle( attachToolbar, 100 );
  93. // Check if the toolbar should be displayed each time view is rendered.
  94. editor.listenTo( editingView, 'render', () => {
  95. const selectedElement = editingView.selection.getSelectedElement();
  96. if ( selectedElement && isImageWidget( selectedElement ) ) {
  97. attachToolbar();
  98. editor.ui.view.listenTo( global.window, 'scroll', attachToolbarCallback );
  99. editor.ui.view.listenTo( global.window, 'resize', attachToolbarCallback );
  100. } else {
  101. panel.hide();
  102. editor.ui.view.stopListening( global.window, 'scroll', attachToolbarCallback );
  103. editor.ui.view.stopListening( global.window, 'resize', attachToolbarCallback );
  104. }
  105. }, { priority: 'low' } );
  106. function attachToolbar() {
  107. panel.attachTo( {
  108. target: editingView.domConverter.viewRangeToDom( editingView.selection.getFirstRange() ),
  109. positions: [ positions.north, positions.south ]
  110. } );
  111. }
  112. return Promise.all( promises );
  113. } );
  114. }
  115. }