linkformview.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /**
  2. * @license Copyright (c) 2003-2019, 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/linkformview
  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 SwitchButtonView from '@ckeditor/ckeditor5-ui/src/button/switchbuttonview';
  12. import LabeledInputView from '@ckeditor/ckeditor5-ui/src/labeledinput/labeledinputview';
  13. import InputTextView from '@ckeditor/ckeditor5-ui/src/inputtext/inputtextview';
  14. import submitHandler from '@ckeditor/ckeditor5-ui/src/bindings/submithandler';
  15. import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
  16. import FocusCycler from '@ckeditor/ckeditor5-ui/src/focuscycler';
  17. import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
  18. import checkIcon from '@ckeditor/ckeditor5-core/theme/icons/check.svg';
  19. import cancelIcon from '@ckeditor/ckeditor5-core/theme/icons/cancel.svg';
  20. import '../../theme/linkform.css';
  21. /**
  22. * The link form view controller class.
  23. *
  24. * See {@link module:link/ui/linkformview~LinkFormView}.
  25. *
  26. * @extends module:ui/view~View
  27. */
  28. export default class LinkFormView extends View {
  29. /**
  30. * Creates an instance of the {@link module:link/ui/linkformview~LinkFormView} class.
  31. *
  32. * Also see {@link #render}.
  33. *
  34. * @param {module:utils/locale~Locale} [locale] The localization services instance.
  35. * @param {module:utils/collection~Collection} [customAttributes] Reference to custom attributes in
  36. * {@link module:link/linkcommand~LinkCommand#customAttributes}.
  37. */
  38. constructor( locale, customAttributes ) {
  39. super( locale );
  40. const t = locale.t;
  41. /**
  42. * Tracks information about DOM focus in the form.
  43. *
  44. * @readonly
  45. * @member {module:utils/focustracker~FocusTracker}
  46. */
  47. this.focusTracker = new FocusTracker();
  48. /**
  49. * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
  50. *
  51. * @readonly
  52. * @member {module:utils/keystrokehandler~KeystrokeHandler}
  53. */
  54. this.keystrokes = new KeystrokeHandler();
  55. /**
  56. * The URL input view.
  57. *
  58. * @member {module:ui/labeledinput/labeledinputview~LabeledInputView}
  59. */
  60. this.urlInputView = this._createUrlInput();
  61. /**
  62. * The Save button view.
  63. *
  64. * @member {module:ui/button/buttonview~ButtonView}
  65. */
  66. this.saveButtonView = this._createButton( t( 'Save' ), checkIcon, 'ck-button-save' );
  67. this.saveButtonView.type = 'submit';
  68. /**
  69. * The Cancel button view.
  70. *
  71. * @member {module:ui/button/buttonview~ButtonView}
  72. */
  73. this.cancelButtonView = this._createButton( t( 'Cancel' ), cancelIcon, 'ck-button-cancel', 'cancel' );
  74. /**
  75. * Keeps reference to {@link module:link/linkcommand~LinkCommand#customAttributes}.
  76. *
  77. * @readonly
  78. * @type {model:utils/collection~Collection}
  79. */
  80. this.customAttributes = customAttributes;
  81. /**
  82. * Keeps reference to {@link module:ui/button/switchbuttonview~SwitchButtonView} made based on {@link #customAttributes}.
  83. * It use {@link #_createCustomAttributesView} to generate proper collection.
  84. *
  85. * @readonly
  86. * @type {module:ui/viewcollection~ViewCollection}
  87. */
  88. this.customAttributesView = this._createCustomAttributesView();
  89. /**
  90. * Collection of views used as children elements in {@link module:link/ui/linkformview~LinkFormView}.
  91. *
  92. * @readonly
  93. * @type {module:ui/viewcollection~ViewCollection}
  94. */
  95. this.children = this._createFormChildren();
  96. /**
  97. * A collection of views which can be focused in the form.
  98. *
  99. * @readonly
  100. * @protected
  101. * @member {module:ui/viewcollection~ViewCollection}
  102. */
  103. this._focusables = new ViewCollection();
  104. /**
  105. * Helps cycling over {@link #_focusables} in the form.
  106. *
  107. * @readonly
  108. * @protected
  109. * @member {module:ui/focuscycler~FocusCycler}
  110. */
  111. this._focusCycler = new FocusCycler( {
  112. focusables: this._focusables,
  113. focusTracker: this.focusTracker,
  114. keystrokeHandler: this.keystrokes,
  115. actions: {
  116. // Navigate form fields backwards using the Shift + Tab keystroke.
  117. focusPrevious: 'shift + tab',
  118. // Navigate form fields forwards using the Tab key.
  119. focusNext: 'tab'
  120. }
  121. } );
  122. this.setTemplate( {
  123. tag: 'form',
  124. attributes: {
  125. class: [
  126. 'ck',
  127. 'ck-link-form',
  128. ],
  129. // https://github.com/ckeditor/ckeditor5-link/issues/90
  130. tabindex: '-1'
  131. },
  132. children: this.children
  133. } );
  134. }
  135. /**
  136. * @inheritDoc
  137. */
  138. render() {
  139. super.render();
  140. submitHandler( {
  141. view: this
  142. } );
  143. // Focus order should be different than position in DOM. Save/Cancel buttons should be focused at the end.
  144. const childViews = [
  145. this.urlInputView,
  146. ...this.customAttributesView,
  147. this.saveButtonView,
  148. this.cancelButtonView,
  149. ];
  150. childViews.forEach( v => {
  151. // Register the view as focusable.
  152. this._focusables.add( v );
  153. // Register the view in the focus tracker.
  154. this.focusTracker.add( v.element );
  155. } );
  156. // Start listening for the keystrokes coming from #element.
  157. this.keystrokes.listenTo( this.element );
  158. }
  159. /**
  160. * Focuses the fist {@link #_focusables} in the form.
  161. */
  162. focus() {
  163. this._focusCycler.focusFirst();
  164. }
  165. /**
  166. * Creates a labeled input view.
  167. *
  168. * @private
  169. * @returns {module:ui/labeledinput/labeledinputview~LabeledInputView} Labeled input view instance.
  170. */
  171. _createUrlInput() {
  172. const t = this.locale.t;
  173. const labeledInput = new LabeledInputView( this.locale, InputTextView );
  174. labeledInput.label = t( 'Link URL' );
  175. labeledInput.inputView.placeholder = 'https://example.com';
  176. return labeledInput;
  177. }
  178. /**
  179. * Creates a button view.
  180. *
  181. * @private
  182. * @param {String} label The button label.
  183. * @param {String} icon The button's icon.
  184. * @param {String} className The additional button CSS class name.
  185. * @param {String} [eventName] An event name that the `ButtonView#execute` event will be delegated to.
  186. * @returns {module:ui/button/buttonview~ButtonView} The button view instance.
  187. */
  188. _createButton( label, icon, className, eventName ) {
  189. const button = new ButtonView( this.locale );
  190. button.set( {
  191. label,
  192. icon,
  193. tooltip: true
  194. } );
  195. button.extendTemplate( {
  196. attributes: {
  197. class: className
  198. }
  199. } );
  200. if ( eventName ) {
  201. button.delegate( 'execute' ).to( this, eventName );
  202. }
  203. return button;
  204. }
  205. /**
  206. * Prepare {@link module:ui/viewcollection~ViewCollection} of {@link module:ui/button/switchbuttonview~SwitchButtonView}
  207. * made based on {@link #customAttributes}
  208. *
  209. * @returns {module:ui/viewcollection~ViewCollection} of Switch Buttons.
  210. */
  211. _createCustomAttributesView() {
  212. const switches = this.createCollection();
  213. const t = this.locale.t;
  214. switches.bindTo( this.customAttributes ).using( item => {
  215. const switchButton = new SwitchButtonView( this.locale );
  216. switchButton.set( {
  217. name: item.id,
  218. label: t( item.label ),
  219. withText: true
  220. } );
  221. switchButton.bind( 'isOn' ).to( item, 'value' );
  222. switchButton.on( 'execute', () => {
  223. item.set( 'value', !switchButton.isOn );
  224. } );
  225. return switchButton;
  226. } );
  227. return switches;
  228. }
  229. /**
  230. * Creates {@link #children} for {@link module:link/ui/linkformview~LinkFormView}. If there exist {@link #customAttributes},
  231. * Then additional View wrapping all {@link #customAttributesView} will be added as a child of LinkFormView.
  232. *
  233. * @returns {module:ui/viewcollection~ViewCollection} children of LinkFormView.
  234. */
  235. _createFormChildren() {
  236. const children = this.createCollection();
  237. children.add( this.urlInputView );
  238. children.add( this.saveButtonView );
  239. children.add( this.cancelButtonView );
  240. if ( this.customAttributes.length ) {
  241. const additionalButtonsView = new View();
  242. additionalButtonsView.setTemplate( {
  243. tag: 'div',
  244. children: [ ...this.customAttributesView ],
  245. attributes: {
  246. class: 'ck-link-form_additional-buttons'
  247. }
  248. } );
  249. children.add( additionalButtonsView );
  250. }
  251. return children;
  252. }
  253. }
  254. /**
  255. * Fired when the form view is submitted (when one of the children triggered the submit event),
  256. * e.g. click on {@link #saveButtonView}.
  257. *
  258. * @event submit
  259. */
  260. /**
  261. * Fired when the form view is canceled, e.g. click on {@link #cancelButtonView}.
  262. *
  263. * @event cancel
  264. */