linkactionsview.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /**
  6. * @module link/ui/linkactionsview
  7. */
  8. import View from '@ckeditor/ckeditor5-ui/src/view';
  9. import ViewCollection from '@ckeditor/ckeditor5-ui/src/viewcollection';
  10. import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
  11. import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
  12. import FocusCycler from '@ckeditor/ckeditor5-ui/src/focuscycler';
  13. import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
  14. import { ensureSafeUrl } from '../utils';
  15. import unlinkIcon from '../../theme/icons/unlink.svg';
  16. import pencilIcon from '@ckeditor/ckeditor5-core/theme/icons/pencil.svg';
  17. import '../../theme/linkactions.css';
  18. /**
  19. * The link actions view class. This view displays the link preview, allows
  20. * unlinking or editing the link.
  21. *
  22. * @extends module:ui/view~View
  23. */
  24. export default class LinkActionsView extends View {
  25. /**
  26. * @inheritDoc
  27. */
  28. constructor( locale ) {
  29. super( locale );
  30. const t = locale.t;
  31. /**
  32. * Tracks information about DOM focus in the actions.
  33. *
  34. * @readonly
  35. * @member {module:utils/focustracker~FocusTracker}
  36. */
  37. this.focusTracker = new FocusTracker();
  38. /**
  39. * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
  40. *
  41. * @readonly
  42. * @member {module:utils/keystrokehandler~KeystrokeHandler}
  43. */
  44. this.keystrokes = new KeystrokeHandler();
  45. /**
  46. * The href preview view.
  47. *
  48. * @member {module:ui/view~View}
  49. */
  50. this.previewButtonView = this._createPreviewButton();
  51. /**
  52. * The unlink button view.
  53. *
  54. * @member {module:ui/button/buttonview~ButtonView}
  55. */
  56. this.unlinkButtonView = this._createButton( t( 'Unlink' ), unlinkIcon, 'unlink' );
  57. /**
  58. * The edit link button view.
  59. *
  60. * @member {module:ui/button/buttonview~ButtonView}
  61. */
  62. this.editButtonView = this._createButton( t( 'Edit link' ), pencilIcon, 'edit' );
  63. /**
  64. * The value of the "href" attribute of the link to use in the {@link #previewButtonView}.
  65. *
  66. * @observable
  67. * @member {String}
  68. */
  69. this.set( 'href' );
  70. /**
  71. * A collection of views that can be focused in the view.
  72. *
  73. * @readonly
  74. * @protected
  75. * @member {module:ui/viewcollection~ViewCollection}
  76. */
  77. this._focusables = new ViewCollection();
  78. /**
  79. * Helps cycling over {@link #_focusables} in the view.
  80. *
  81. * @readonly
  82. * @protected
  83. * @member {module:ui/focuscycler~FocusCycler}
  84. */
  85. this._focusCycler = new FocusCycler( {
  86. focusables: this._focusables,
  87. focusTracker: this.focusTracker,
  88. keystrokeHandler: this.keystrokes,
  89. actions: {
  90. // Navigate fields backwards using the Shift + Tab keystroke.
  91. focusPrevious: 'shift + tab',
  92. // Navigate fields forwards using the Tab key.
  93. focusNext: 'tab'
  94. }
  95. } );
  96. this.setTemplate( {
  97. tag: 'div',
  98. attributes: {
  99. class: [
  100. 'ck',
  101. 'ck-link-actions'
  102. ],
  103. // https://github.com/ckeditor/ckeditor5-link/issues/90
  104. tabindex: '-1'
  105. },
  106. children: [
  107. this.previewButtonView,
  108. this.editButtonView,
  109. this.unlinkButtonView
  110. ]
  111. } );
  112. }
  113. /**
  114. * @inheritDoc
  115. */
  116. render() {
  117. super.render();
  118. const childViews = [
  119. this.previewButtonView,
  120. this.editButtonView,
  121. this.unlinkButtonView
  122. ];
  123. childViews.forEach( v => {
  124. // Register the view as focusable.
  125. this._focusables.add( v );
  126. // Register the view in the focus tracker.
  127. this.focusTracker.add( v.element );
  128. } );
  129. // Start listening for the keystrokes coming from #element.
  130. this.keystrokes.listenTo( this.element );
  131. }
  132. /**
  133. * Focuses the fist {@link #_focusables} in the actions.
  134. */
  135. focus() {
  136. this._focusCycler.focusFirst();
  137. }
  138. /**
  139. * Creates a button view.
  140. *
  141. * @private
  142. * @param {String} label The button label.
  143. * @param {String} icon The button icon.
  144. * @param {String} [eventName] An event name that the `ButtonView#execute` event will be delegated to.
  145. * @returns {module:ui/button/buttonview~ButtonView} The button view instance.
  146. */
  147. _createButton( label, icon, eventName ) {
  148. const button = new ButtonView( this.locale );
  149. button.set( {
  150. label,
  151. icon,
  152. tooltip: true
  153. } );
  154. button.delegate( 'execute' ).to( this, eventName );
  155. return button;
  156. }
  157. /**
  158. * Creates a link href preview button.
  159. *
  160. * @private
  161. * @returns {module:ui/button/buttonview~ButtonView} The button view instance.
  162. */
  163. _createPreviewButton() {
  164. const button = new ButtonView( this.locale );
  165. const bind = this.bindTemplate;
  166. const t = this.t;
  167. button.set( {
  168. withText: true,
  169. tooltip: t( 'Open link in new tab' )
  170. } );
  171. button.extendTemplate( {
  172. attributes: {
  173. class: [
  174. 'ck',
  175. 'ck-link-actions__preview'
  176. ],
  177. href: bind.to( 'href', href => href && ensureSafeUrl( href ) ),
  178. target: '_blank',
  179. rel: 'noopener noreferrer'
  180. }
  181. } );
  182. button.bind( 'label' ).to( this, 'href', href => {
  183. return href || t( 'This link has no URL' );
  184. } );
  185. button.bind( 'isEnabled' ).to( this, 'href', href => !!href );
  186. button.template.tag = 'a';
  187. button.template.eventListeners = {};
  188. return button;
  189. }
  190. }
  191. /**
  192. * Fired when the {@link #editButtonView} is clicked.
  193. *
  194. * @event edit
  195. */
  196. /**
  197. * Fired when the {@link #unlinkButtonView} is clicked.
  198. *
  199. * @event unlink
  200. */