file-upload.js

  1. import { queryOne, formatBytes } from '@ecl/dom-utils';
  2. /**
  3. * @param {HTMLElement} element DOM element for component instantiation and scope
  4. * @param {Object} options
  5. * @param {String} options.groupSelector Selector for file upload form group
  6. * @param {String} options.buttonSelector Selector for file upload button
  7. * @param {String} options.listSelector Selector for list of file names
  8. * @param {String} options.labelChoose Label choose state
  9. * @param {String} options.labelReplace Label replace state
  10. * @param {Boolean} options.attachChangeListener Whether or not to bind change events on toggle
  11. */
  12. export class FileUpload {
  13. /**
  14. * @static
  15. * Shorthand for instance creation and initialisation.
  16. *
  17. * @param {HTMLElement} root DOM element for component instantiation and scope
  18. *
  19. * @return {FileUpload} An instance of FileUpload.
  20. */
  21. static autoInit(root, { FILE_UPLOAD: defaultOptions = {} } = {}) {
  22. const fileUpload = new FileUpload(root, defaultOptions);
  23. fileUpload.init();
  24. root.ECLFileUpload = fileUpload;
  25. return fileUpload;
  26. }
  27. constructor(
  28. element,
  29. {
  30. groupSelector = '[data-ecl-file-upload-group]',
  31. buttonSelector = '[data-ecl-file-upload-button]',
  32. listSelector = '[data-ecl-file-upload-list]',
  33. labelChoose = 'data-ecl-file-upload-label-choose',
  34. labelReplace = 'data-ecl-file-upload-label-replace',
  35. attachChangeListener = true,
  36. } = {},
  37. ) {
  38. // Check element
  39. if (!element || element.nodeType !== Node.ELEMENT_NODE) {
  40. throw new TypeError(
  41. 'DOM element should be given to initialize this widget.',
  42. );
  43. }
  44. this.element = element;
  45. // Options
  46. this.groupSelector = groupSelector;
  47. this.buttonSelector = buttonSelector;
  48. this.listSelector = listSelector;
  49. this.labelChoose = labelChoose;
  50. this.labelReplace = labelReplace;
  51. this.attachChangeListener = attachChangeListener;
  52. // Private variables
  53. this.fileUploadGroup = null;
  54. this.fileUploadInput = null;
  55. this.fileUploadButton = null;
  56. this.fileUploadList = null;
  57. // Bind `this` for use in callbacks
  58. this.handleChange = this.handleChange.bind(this);
  59. }
  60. /**
  61. * Initialise component.
  62. */
  63. init() {
  64. if (!ECL) {
  65. throw new TypeError('Called init but ECL is not present');
  66. }
  67. ECL.components = ECL.components || new Map();
  68. this.fileUploadGroup = this.element.closest(this.groupSelector);
  69. this.fileUploadInput = this.element;
  70. this.fileUploadButton = queryOne(this.buttonSelector, this.fileUploadGroup);
  71. this.fileUploadList = queryOne(this.listSelector, this.fileUploadGroup);
  72. // Bind events on input
  73. if (this.attachChangeListener && this.fileUploadInput) {
  74. this.fileUploadInput.addEventListener('change', this.handleChange);
  75. }
  76. // Set ecl initialized attribute
  77. this.element.setAttribute('data-ecl-auto-initialized', 'true');
  78. ECL.components.set(this.element, this);
  79. }
  80. /**
  81. * Destroy component.
  82. */
  83. destroy() {
  84. if (this.attachChangeListener && this.fileUploadInput) {
  85. this.fileUploadInput.removeEventListener('change', this.handleChange);
  86. }
  87. if (this.element) {
  88. this.element.removeAttribute('data-ecl-auto-initialized');
  89. ECL.components.delete(this.element);
  90. }
  91. }
  92. /**
  93. * @param {Event} e
  94. */
  95. handleChange(e) {
  96. if (!('files' in e.target)) {
  97. if (this.fileUploadButton.hasAttribute(this.labelChoose)) {
  98. this.fileUploadButton.innerHTML = this.fileUploadButton.getAttribute(
  99. this.labelChoose,
  100. );
  101. }
  102. return;
  103. }
  104. let fileList = '';
  105. // Get file names
  106. Array.prototype.forEach.call(e.target.files, (file) => {
  107. const fileSize = formatBytes(file.size, 1);
  108. const fileExtension = file.name.split('.').pop();
  109. fileList += `<li class="ecl-file-upload__item">
  110. <span class="ecl-file-upload__item-name">${file.name}</span>
  111. <span class="ecl-file-upload__item-meta">(${fileSize} - ${fileExtension})</span>
  112. </li>`;
  113. });
  114. // Update file list
  115. this.fileUploadList.innerHTML = fileList;
  116. // Update button label
  117. if (this.fileUploadButton.hasAttribute(this.labelReplace)) {
  118. this.fileUploadButton.innerHTML = this.fileUploadButton.getAttribute(
  119. this.labelReplace,
  120. );
  121. }
  122. }
  123. }
  124. export default FileUpload;