UIPlugin.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. "use strict";
  2. var _preact = require("preact");
  3. function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
  4. var id = 0;
  5. function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; }
  6. const findDOMElement = require("@uppy/utils/lib/findDOMElement");
  7. const getTextDirection = require("@uppy/utils/lib/getTextDirection");
  8. const BasePlugin = require("./BasePlugin.js");
  9. /**
  10. * Defer a frequent call to the microtask queue.
  11. *
  12. * @param {() => T} fn
  13. * @returns {Promise<T>}
  14. */
  15. function debounce(fn) {
  16. let calling = null;
  17. let latestArgs = null;
  18. return function () {
  19. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  20. args[_key] = arguments[_key];
  21. }
  22. latestArgs = args;
  23. if (!calling) {
  24. calling = Promise.resolve().then(() => {
  25. calling = null; // At this point `args` may be different from the most
  26. // recent state, if multiple calls happened since this task
  27. // was queued. So we use the `latestArgs`, which definitely
  28. // is the most recent call.
  29. return fn(...latestArgs);
  30. });
  31. }
  32. return calling;
  33. };
  34. }
  35. /**
  36. * UIPlugin is the extended version of BasePlugin to incorporate rendering with Preact.
  37. * Use this for plugins that need a user interface.
  38. *
  39. * For plugins without an user interface, see BasePlugin.
  40. */
  41. var _updateUI = /*#__PURE__*/_classPrivateFieldLooseKey("updateUI");
  42. class UIPlugin extends BasePlugin {
  43. constructor() {
  44. super(...arguments);
  45. Object.defineProperty(this, _updateUI, {
  46. writable: true,
  47. value: void 0
  48. });
  49. }
  50. /**
  51. * Check if supplied `target` is a DOM element or an `object`.
  52. * If it’s an object — target is a plugin, and we search `plugins`
  53. * for a plugin with same name and return its target.
  54. */
  55. mount(target, plugin) {
  56. const callerPluginName = plugin.id;
  57. const targetElement = findDOMElement(target);
  58. if (targetElement) {
  59. this.isTargetDOMEl = true; // When target is <body> with a single <div> element,
  60. // Preact thinks it’s the Uppy root element in there when doing a diff,
  61. // and destroys it. So we are creating a fragment (could be empty div)
  62. const uppyRootElement = document.createElement('div');
  63. uppyRootElement.classList.add('uppy-Root'); // API for plugins that require a synchronous rerender.
  64. _classPrivateFieldLooseBase(this, _updateUI)[_updateUI] = debounce(state => {
  65. // plugin could be removed, but this.rerender is debounced below,
  66. // so it could still be called even after uppy.removePlugin or uppy.close
  67. // hence the check
  68. if (!this.uppy.getPlugin(this.id)) return;
  69. (0, _preact.render)(this.render(state), uppyRootElement);
  70. this.afterUpdate();
  71. });
  72. this.uppy.log(`Installing ${callerPluginName} to a DOM element '${target}'`);
  73. if (this.opts.replaceTargetContent) {
  74. // Doing render(h(null), targetElement), which should have been
  75. // a better way, since because the component might need to do additional cleanup when it is removed,
  76. // stopped working — Preact just adds null into target, not replacing
  77. targetElement.innerHTML = '';
  78. }
  79. (0, _preact.render)(this.render(this.uppy.getState()), uppyRootElement);
  80. this.el = uppyRootElement;
  81. targetElement.appendChild(uppyRootElement); // Set the text direction if the page has not defined one.
  82. uppyRootElement.dir = this.opts.direction || getTextDirection(uppyRootElement) || 'ltr';
  83. this.onMount();
  84. return this.el;
  85. }
  86. let targetPlugin;
  87. if (typeof target === 'object' && target instanceof UIPlugin) {
  88. // Targeting a plugin *instance*
  89. targetPlugin = target;
  90. } else if (typeof target === 'function') {
  91. // Targeting a plugin type
  92. const Target = target; // Find the target plugin instance.
  93. this.uppy.iteratePlugins(p => {
  94. if (p instanceof Target) {
  95. targetPlugin = p;
  96. }
  97. });
  98. }
  99. if (targetPlugin) {
  100. this.uppy.log(`Installing ${callerPluginName} to ${targetPlugin.id}`);
  101. this.parent = targetPlugin;
  102. this.el = targetPlugin.addTarget(plugin);
  103. this.onMount();
  104. return this.el;
  105. }
  106. this.uppy.log(`Not installing ${callerPluginName}`);
  107. let message = `Invalid target option given to ${callerPluginName}.`;
  108. if (typeof target === 'function') {
  109. message += ' The given target is not a Plugin class. ' + 'Please check that you\'re not specifying a React Component instead of a plugin. ' + 'If you are using @uppy/* packages directly, make sure you have only 1 version of @uppy/core installed: ' + 'run `npm ls @uppy/core` on the command line and verify that all the versions match and are deduped correctly.';
  110. } else {
  111. message += 'If you meant to target an HTML element, please make sure that the element exists. ' + 'Check that the <script> tag initializing Uppy is right before the closing </body> tag at the end of the page. ' + '(see https://github.com/transloadit/uppy/issues/1042)\n\n' + 'If you meant to target a plugin, please confirm that your `import` statements or `require` calls are correct.';
  112. }
  113. throw new Error(message);
  114. }
  115. update(state) {
  116. if (this.el != null) {
  117. var _classPrivateFieldLoo, _classPrivateFieldLoo2;
  118. (_classPrivateFieldLoo = (_classPrivateFieldLoo2 = _classPrivateFieldLooseBase(this, _updateUI))[_updateUI]) == null ? void 0 : _classPrivateFieldLoo.call(_classPrivateFieldLoo2, state);
  119. }
  120. }
  121. unmount() {
  122. if (this.isTargetDOMEl) {
  123. var _this$el;
  124. (_this$el = this.el) == null ? void 0 : _this$el.remove();
  125. }
  126. this.onUnmount();
  127. } // eslint-disable-next-line class-methods-use-this
  128. onMount() {} // eslint-disable-next-line class-methods-use-this
  129. onUnmount() {}
  130. }
  131. module.exports = UIPlugin;