diff --git a/.eslintrc.js b/.eslintrc.js index 342e74be95..f928f0c33d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -213,6 +213,7 @@ module.exports = { 'packages/@uppy/progress-bar/src/**/*.js', 'packages/@uppy/svelte/src/**/*.js', 'packages/@uppy/svelte/rollup.config.js', + 'packages/@uppy/url/src/**/*.js', 'packages/@uppy/vue/src/**/*.js', 'packages/@uppy/webcam/src/**/*.js', ], diff --git a/packages/@uppy/url/package.json b/packages/@uppy/url/package.json index 93094eee78..9091f42d8b 100644 --- a/packages/@uppy/url/package.json +++ b/packages/@uppy/url/package.json @@ -6,6 +6,7 @@ "main": "lib/index.js", "style": "dist/style.min.css", "types": "types/index.d.ts", + "type": "module", "keywords": [ "file uploader", "url", diff --git a/packages/@uppy/url/src/Url.jsx b/packages/@uppy/url/src/Url.jsx new file mode 100644 index 0000000000..c5551123f6 --- /dev/null +++ b/packages/@uppy/url/src/Url.jsx @@ -0,0 +1,192 @@ +import { h } from 'preact' +import { UIPlugin } from '@uppy/core' +import { RequestClient } from '@uppy/companion-client' +import toArray from '@uppy/utils/lib/toArray' +import UrlUI from './UrlUI.jsx' +import forEachDroppedOrPastedUrl from './utils/forEachDroppedOrPastedUrl.js' + +import packageJson from '../package.json' +import locale from './locale.js' + +function UrlIcon () { + return ( + + ) +} + +function addProtocolToURL (url) { + const protocolRegex = /^[a-z0-9]+:\/\// + const defaultProtocol = 'http://' + if (protocolRegex.test(url)) { + return url + } + + return defaultProtocol + url +} + +function canHandleRootDrop (e) { + const items = toArray(e.dataTransfer.items) + const urls = items.filter((item) => item.kind === 'string' + && item.type === 'text/uri-list') + return urls.length > 0 +} + +function checkIfCorrectURL (url) { + if (!url) return false + + const protocol = url.match(/^([a-z0-9]+):\/\//)[1] + if (protocol !== 'http' && protocol !== 'https') { + return false + } + + return true +} + +function getFileNameFromUrl (url) { + return url.substring(url.lastIndexOf('/') + 1) +} +/** + * Url + * + */ +export default class Url extends UIPlugin { + static VERSION = packageJson.version + + constructor (uppy, opts) { + super(uppy, opts) + this.id = this.opts.id || 'Url' + this.title = this.opts.title || 'Link' + this.type = 'acquirer' + this.icon = () => + + // Set default options and locale + this.defaultLocale = locale + + const defaultOptions = {} + + this.opts = { ...defaultOptions, ...opts } + + this.i18nInit() + + this.hostname = this.opts.companionUrl + + if (!this.hostname) { + throw new Error('Companion hostname is required, please consult https://uppy.io/docs/companion') + } + + // Bind all event handlers for referencability + this.getMeta = this.getMeta.bind(this) + this.addFile = this.addFile.bind(this) + this.handleRootDrop = this.handleRootDrop.bind(this) + this.handleRootPaste = this.handleRootPaste.bind(this) + + this.client = new RequestClient(uppy, { + companionUrl: this.opts.companionUrl, + companionHeaders: this.opts.companionHeaders, + companionCookiesRule: this.opts.companionCookiesRule, + }) + } + + getMeta (url) { + return this.client.post('url/meta', { url }) + .then((res) => { + if (res.error) { + this.uppy.log('[URL] Error:') + this.uppy.log(res.error) + throw new Error('Failed to fetch the file') + } + return res + }) + } + + async addFile (protocollessUrl) { + const url = this.addProtocolToURL(protocollessUrl) + if (!this.checkIfCorrectURL(url)) { + this.uppy.log(`[URL] Incorrect URL entered: ${url}`) + this.uppy.info(this.i18n('enterCorrectUrl'), 'error', 4000) + return undefined + } + + try { + const meta = await this.getMeta(url) + + const tagFile = { + source: this.id, + name: this.getFileNameFromUrl(url), + type: meta.type, + data: { + size: meta.size, + }, + isRemote: true, + body: { + url, + }, + remote: { + companionUrl: this.opts.companionUrl, + url: `${this.hostname}/url/get`, + body: { + fileId: url, + url, + }, + providerOptions: this.client.opts, + }, + } + this.uppy.log('[Url] Adding remote file') + try { + return this.uppy.addFile(tagFile) + } catch (err) { + if (!err.isRestriction) { + this.uppy.log(err) + } + return err + } + } catch (err) { + this.uppy.log(err) + this.uppy.info({ + message: this.i18n('failedToFetch'), + details: err, + }, 'error', 4000) + return err + } + } + + handleRootDrop (e) { + forEachDroppedOrPastedUrl(e.dataTransfer, 'drop', (url) => { + this.uppy.log(`[URL] Adding file from dropped url: ${url}`) + this.addFile(url) + }) + } + + handleRootPaste (e) { + forEachDroppedOrPastedUrl(e.clipboardData, 'paste', (url) => { + this.uppy.log(`[URL] Adding file from pasted url: ${url}`) + this.addFile(url) + }) + } + + render () { + return + } + + install () { + const { target } = this.opts + if (target) { + this.mount(target, this) + } + } + + uninstall () { + this.unmount() + } +} + +// TODO: remove from prototype in the next major. +Url.prototype.addProtocolToURL = addProtocolToURL +Url.prototype.canHandleRootDrop = canHandleRootDrop +Url.prototype.checkIfCorrectURL = checkIfCorrectURL +Url.prototype.getFileNameFromUrl = getFileNameFromUrl diff --git a/packages/@uppy/url/src/UrlUI.js b/packages/@uppy/url/src/UrlUI.jsx similarity index 71% rename from packages/@uppy/url/src/UrlUI.js rename to packages/@uppy/url/src/UrlUI.jsx index 99d21f239b..e387faa03b 100644 --- a/packages/@uppy/url/src/UrlUI.js +++ b/packages/@uppy/url/src/UrlUI.jsx @@ -1,4 +1,4 @@ -const { h, Component } = require('preact') +import { h, Component } from 'preact' class UrlUI extends Component { constructor (props) { @@ -12,23 +12,26 @@ class UrlUI extends Component { } handleKeyPress (ev) { + const { addFile } = this.props if (ev.keyCode === 13) { - this.props.addFile(this.input.value) + addFile(this.input.value) } } handleClick () { - this.props.addFile(this.input.value) + const { addFile } = this.props + addFile(this.input.value) } render () { + const { i18n } = this.props return (
{ this.input = input }} data-uppy-super-focusable @@ -38,11 +41,11 @@ class UrlUI extends Component { type="button" onClick={this.handleClick} > - {this.props.i18n('import')} + {i18n('import')}
) } } -module.exports = UrlUI +export default UrlUI diff --git a/packages/@uppy/url/src/index.js b/packages/@uppy/url/src/index.js index 1568e89b9e..a427b2b25e 100644 --- a/packages/@uppy/url/src/index.js +++ b/packages/@uppy/url/src/index.js @@ -1,189 +1 @@ -const { UIPlugin } = require('@uppy/core') -const { h } = require('preact') -const { RequestClient } = require('@uppy/companion-client') -const toArray = require('@uppy/utils/lib/toArray') -const UrlUI = require('./UrlUI.js') -const forEachDroppedOrPastedUrl = require('./utils/forEachDroppedOrPastedUrl') - -const locale = require('./locale') - -function UrlIcon () { - return ( - - ) -} - -/** - * Url - * - */ -module.exports = class Url extends UIPlugin { - static VERSION = require('../package.json').version - - constructor (uppy, opts) { - super(uppy, opts) - this.id = this.opts.id || 'Url' - this.title = this.opts.title || 'Link' - this.type = 'acquirer' - this.icon = () => - - // Set default options and locale - this.defaultLocale = locale - - const defaultOptions = {} - - this.opts = { ...defaultOptions, ...opts } - - this.i18nInit() - - this.hostname = this.opts.companionUrl - - if (!this.hostname) { - throw new Error('Companion hostname is required, please consult https://uppy.io/docs/companion') - } - - // Bind all event handlers for referencability - this.getMeta = this.getMeta.bind(this) - this.addFile = this.addFile.bind(this) - this.handleRootDrop = this.handleRootDrop.bind(this) - this.handleRootPaste = this.handleRootPaste.bind(this) - - this.client = new RequestClient(uppy, { - companionUrl: this.opts.companionUrl, - companionHeaders: this.opts.companionHeaders, - companionCookiesRule: this.opts.companionCookiesRule, - }) - } - - getFileNameFromUrl (url) { - return url.substring(url.lastIndexOf('/') + 1) - } - - checkIfCorrectURL (url) { - if (!url) return false - - const protocol = url.match(/^([a-z0-9]+):\/\//)[1] - if (protocol !== 'http' && protocol !== 'https') { - return false - } - - return true - } - - addProtocolToURL (url) { - const protocolRegex = /^[a-z0-9]+:\/\// - const defaultProtocol = 'http://' - if (protocolRegex.test(url)) { - return url - } - - return defaultProtocol + url - } - - getMeta (url) { - return this.client.post('url/meta', { url }) - .then((res) => { - if (res.error) { - this.uppy.log('[URL] Error:') - this.uppy.log(res.error) - throw new Error('Failed to fetch the file') - } - return res - }) - } - - addFile (url) { - url = this.addProtocolToURL(url) - if (!this.checkIfCorrectURL(url)) { - this.uppy.log(`[URL] Incorrect URL entered: ${url}`) - this.uppy.info(this.i18n('enterCorrectUrl'), 'error', 4000) - return - } - - return this.getMeta(url) - .then((meta) => { - const tagFile = { - source: this.id, - name: this.getFileNameFromUrl(url), - type: meta.type, - data: { - size: meta.size, - }, - isRemote: true, - body: { - url, - }, - remote: { - companionUrl: this.opts.companionUrl, - url: `${this.hostname}/url/get`, - body: { - fileId: url, - url, - }, - providerOptions: this.client.opts, - }, - } - return tagFile - }) - .then((tagFile) => { - this.uppy.log('[Url] Adding remote file') - try { - return this.uppy.addFile(tagFile) - } catch (err) { - if (!err.isRestriction) { - this.uppy.log(err) - } - return err - } - }) - .catch((err) => { - this.uppy.log(err) - this.uppy.info({ - message: this.i18n('failedToFetch'), - details: err, - }, 'error', 4000) - return err - }) - } - - canHandleRootDrop (e) { - const items = toArray(e.dataTransfer.items) - const urls = items.filter((item) => item.kind === 'string' - && item.type === 'text/uri-list') - return urls.length > 0 - } - - handleRootDrop (e) { - forEachDroppedOrPastedUrl(e.dataTransfer, 'drop', (url) => { - this.uppy.log(`[URL] Adding file from dropped url: ${url}`) - this.addFile(url) - }) - } - - handleRootPaste (e) { - forEachDroppedOrPastedUrl(e.clipboardData, 'paste', (url) => { - this.uppy.log(`[URL] Adding file from pasted url: ${url}`) - this.addFile(url) - }) - } - - render () { - return - } - - install () { - const { target } = this.opts - if (target) { - this.mount(target, this) - } - } - - uninstall () { - this.unmount() - } -} +export { default } from './Url.jsx' diff --git a/packages/@uppy/url/src/locale.js b/packages/@uppy/url/src/locale.js index df90c44af0..79350e25aa 100644 --- a/packages/@uppy/url/src/locale.js +++ b/packages/@uppy/url/src/locale.js @@ -1,4 +1,4 @@ -module.exports = { +export default { strings: { // Label for the "Import" button. import: 'Import', diff --git a/packages/@uppy/url/src/utils/forEachDroppedOrPastedUrl.js b/packages/@uppy/url/src/utils/forEachDroppedOrPastedUrl.js index d02f8fc081..a0c700a1a2 100644 --- a/packages/@uppy/url/src/utils/forEachDroppedOrPastedUrl.js +++ b/packages/@uppy/url/src/utils/forEachDroppedOrPastedUrl.js @@ -1,4 +1,4 @@ -const toArray = require('@uppy/utils/lib/toArray') +import toArray from '@uppy/utils/lib/toArray' /* SITUATION @@ -58,7 +58,7 @@ const toArray = require('@uppy/utils/lib/toArray') * @param {string} isDropOrPaste - either 'drop' or 'paste' * @param {Function} callback - (urlString) => {} */ -module.exports = function forEachDroppedOrPastedUrl (dataTransfer, isDropOrPaste, callback) { +export default function forEachDroppedOrPastedUrl (dataTransfer, isDropOrPaste, callback) { const items = toArray(dataTransfer.items) let urlItems diff --git a/website/src/docs/url.md b/website/src/docs/url.md index a2853e493a..025774f0d7 100644 --- a/website/src/docs/url.md +++ b/website/src/docs/url.md @@ -87,7 +87,7 @@ This option correlates to the [RequestCredentials value](https://developer.mozil ```js -module.exports = { +export default { strings: { // Label for the "Import" button. import: 'Import',