filedialogbuttonview.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /**
  2. * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module upload/ui/filedialogbuttonview
  7. */
  8. import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
  9. import View from '@ckeditor/ckeditor5-ui/src/view';
  10. /**
  11. * The file dialog button view.
  12. *
  13. * This component provides a button that opens the native file selection dialog.
  14. * It can be used to implement the UI of a file upload feature.
  15. *
  16. * const view = new FileDialogButtonView( locale );
  17. *
  18. * view.set( {
  19. * acceptedType: 'image/*',
  20. * allowMultipleFiles: true
  21. * } );
  22. *
  23. * view.buttonView.set( {
  24. * label: t( 'Insert image' ),
  25. * icon: imageIcon,
  26. * tooltip: true
  27. * } );
  28. *
  29. * view.on( 'done', ( evt, files ) => {
  30. * for ( const file of Array.from( files ) ) {
  31. * console.log( 'Selected file', file );
  32. * }
  33. * } );
  34. *
  35. * @extends module:ui/view~View
  36. */
  37. export default class FileDialogButtonView extends View {
  38. /**
  39. * @inheritDoc
  40. */
  41. constructor( locale ) {
  42. super( locale );
  43. /**
  44. * The button view of the component.
  45. *
  46. * @member {module:ui/button/buttonview~ButtonView}
  47. */
  48. this.buttonView = new ButtonView( locale );
  49. /**
  50. * A hidden `<input>` view used to execute file dialog.
  51. *
  52. * @protected
  53. * @member {module:upload/ui/filedialogbuttonview~FileInputView}
  54. */
  55. this._fileInputView = new FileInputView( locale );
  56. /**
  57. * Accepted file types. Can be provided in form of file extensions, media type or one of:
  58. * * `audio/*`,
  59. * * `video/*`,
  60. * * `image/*`.
  61. *
  62. * @observable
  63. * @member {String} #acceptedType
  64. */
  65. this._fileInputView.bind( 'acceptedType' ).to( this );
  66. /**
  67. * Indicates if multiple files can be selected. Defaults to `true`.
  68. *
  69. * @observable
  70. * @member {Boolean} #allowMultipleFiles
  71. */
  72. this._fileInputView.bind( 'allowMultipleFiles' ).to( this );
  73. /**
  74. * Fired when file dialog is closed with file selected.
  75. *
  76. * view.on( 'done', ( evt, files ) => {
  77. * for ( const file of files ) {
  78. * console.log( 'Selected file', file );
  79. * }
  80. * }
  81. *
  82. * @event done
  83. * @param {Array.<File>} files Array of selected files.
  84. */
  85. this._fileInputView.delegate( 'done' ).to( this );
  86. this.setTemplate( {
  87. tag: 'span',
  88. attributes: {
  89. class: 'ck-file-dialog-button',
  90. },
  91. children: [
  92. this.buttonView,
  93. this._fileInputView
  94. ]
  95. } );
  96. this.buttonView.on( 'execute', () => {
  97. this._fileInputView.open();
  98. } );
  99. }
  100. /**
  101. * Focuses the {@link #buttonView}.
  102. */
  103. focus() {
  104. this.buttonView.focus();
  105. }
  106. }
  107. /**
  108. * The hidden file input view class.
  109. *
  110. * @private
  111. * @extends {module:ui/view~View}
  112. */
  113. class FileInputView extends View {
  114. /**
  115. * @inheritDoc
  116. */
  117. constructor( locale ) {
  118. super( locale );
  119. /**
  120. * Accepted file types. Can be provided in form of file extensions, media type or one of:
  121. * * `audio/*`,
  122. * * `video/*`,
  123. * * `image/*`.
  124. *
  125. * @observable
  126. * @member {String} #acceptedType
  127. */
  128. this.set( 'acceptedType' );
  129. /**
  130. * Indicates if multiple files can be selected. Defaults to `false`.
  131. *
  132. * @observable
  133. * @member {Boolean} #allowMultipleFiles
  134. */
  135. this.set( 'allowMultipleFiles', false );
  136. const bind = this.bindTemplate;
  137. this.setTemplate( {
  138. tag: 'input',
  139. attributes: {
  140. class: [
  141. 'ck-hidden'
  142. ],
  143. type: 'file',
  144. tabindex: '-1',
  145. accept: bind.to( 'acceptedType' ),
  146. multiple: bind.to( 'allowMultipleFiles' )
  147. },
  148. on: {
  149. // Removing from code coverage since we cannot programmatically set input element files.
  150. change: bind.to( /* istanbul ignore next */ () => {
  151. if ( this.element && this.element.files && this.element.files.length ) {
  152. this.fire( 'done', this.element.files );
  153. }
  154. this.element.value = '';
  155. } )
  156. }
  157. } );
  158. }
  159. /**
  160. * Opens file dialog.
  161. */
  162. open() {
  163. this.element.click();
  164. }
  165. }