浏览代码

Merge pull request #1597 from transloadit/fix/onBeforeFileAdded-not-called

Fix/on before file added not called
Evgenia Karunus 5 年之前
父节点
当前提交
87b64a1723

+ 0 - 47
packages/@uppy/dashboard/src/components/ActionBrowseTagline.js

@@ -1,47 +0,0 @@
-const { h, Component } = require('preact')
-
-class ActionBrowseTagline extends Component {
-  constructor (props) {
-    super(props)
-    this.handleClick = this.handleClick.bind(this)
-  }
-
-  handleClick (ev) {
-    this.input.click()
-  }
-
-  render () {
-    const browse = (
-      <button type="button" class="uppy-Dashboard-browse" onclick={this.handleClick}>
-        {this.props.i18n('browse')}
-      </button>
-    )
-
-    // empty value="" on file input, so that the input is cleared after a file is selected,
-    // because Uppy will be handling the upload and so we can select same file
-    // after removing — otherwise browser thinks it’s already selected
-    return (
-      <div class="uppy-Dashboard-dropFilesTitle">
-        {this.props.acquirers.length === 0
-          ? this.props.i18nArray('dropPaste', { browse })
-          : this.props.i18nArray('dropPasteImport', { browse })
-        }
-        <input class="uppy-Dashboard-input"
-          hidden
-          aria-hidden="true"
-          tabindex={-1}
-          type="file"
-          name="files[]"
-          multiple={this.props.maxNumberOfFiles !== 1}
-          onchange={this.props.handleInputChange}
-          accept={this.props.allowedFileTypes}
-          value=""
-          ref={(input) => {
-            this.input = input
-          }} />
-      </div>
-    )
-  }
-}
-
-module.exports = ActionBrowseTagline

+ 115 - 79
packages/@uppy/dashboard/src/components/AddFiles.js

@@ -1,101 +1,137 @@
-const ActionBrowseTagline = require('./ActionBrowseTagline')
 const { localIcon } = require('./icons')
 const { h, Component } = require('preact')
 
-const poweredByUppy = (props) => {
-  return <a tabindex="-1" href="https://uppy.io" rel="noreferrer noopener" target="_blank" class="uppy-Dashboard-poweredBy">{props.i18n('poweredBy')} <svg aria-hidden="true" class="UppyIcon uppy-Dashboard-poweredByIcon" width="11" height="11" viewBox="0 0 11 11">
-    <path d="M7.365 10.5l-.01-4.045h2.612L5.5.806l-4.467 5.65h2.604l.01 4.044h3.718z" fill-rule="evenodd" />
-  </svg><span class="uppy-Dashboard-poweredByUppy">Uppy</span></a>
-}
 class AddFiles extends Component {
   constructor (props) {
     super(props)
-    this.handleClick = this.handleClick.bind(this)
+
+    this.triggerFileInputClick = this.triggerFileInputClick.bind(this)
+    this.handleFileInputChange = this.handleFileInputChange.bind(this)
+
+    this.renderPoweredByUppy = this.renderPoweredByUppy.bind(this)
+    this.renderHiddenFileInput = this.renderHiddenFileInput.bind(this)
+    this.renderDropPasteBrowseTagline = this.renderDropPasteBrowseTagline.bind(this)
+    this.renderMyDeviceAcquirer = this.renderMyDeviceAcquirer.bind(this)
+    this.renderAcquirer = this.renderAcquirer.bind(this)
   }
 
-  handleClick (ev) {
-    this.input.click()
+  triggerFileInputClick () {
+    this.fileInput.click()
   }
 
-  render () {
-    const hasAcquirers = this.props.acquirers.length !== 0
+  handleFileInputChange (event) {
+    this.props.handleInputChange(event)
 
-    if (!hasAcquirers) {
-      return (
-        <div class="uppy-DashboardAddFiles">
-          <div class="uppy-DashboardTabs">
-            <ActionBrowseTagline
-              acquirers={this.props.acquirers}
-              handleInputChange={this.props.handleInputChange}
-              i18n={this.props.i18n}
-              i18nArray={this.props.i18nArray}
-              allowedFileTypes={this.props.allowedFileTypes}
-              maxNumberOfFiles={this.props.maxNumberOfFiles}
-            />
-          </div>
-          <div class="uppy-DashboardAddFiles-info">
-            { this.props.note && <div class="uppy-Dashboard-note">{this.props.note}</div> }
-            { this.props.proudlyDisplayPoweredByUppy && poweredByUppy(this.props) }
-          </div>
-        </div>
-      )
-    }
+    // We clear the input after a file is selected, because otherwise
+    // change event is not fired in Chrome and Safari when a file
+    // with the same name is selected.
+    // ___Why not use value="" on <input/> instead?
+    //    Because if we use that method of clearing the input,
+    //    Chrome will not trigger change if we drop the same file twice (Issue #768).
+    event.target.value = null
+  }
+
+  renderPoweredByUppy () {
+    return (
+      <a
+        tabindex="-1"
+        href="https://uppy.io"
+        rel="noreferrer noopener"
+        target="_blank"
+        class="uppy-Dashboard-poweredBy">
+        {this.props.i18n('poweredBy') + ' '}
+        <svg aria-hidden="true" class="UppyIcon uppy-Dashboard-poweredByIcon" width="11" height="11" viewBox="0 0 11 11">
+          <path d="M7.365 10.5l-.01-4.045h2.612L5.5.806l-4.467 5.65h2.604l.01 4.044h3.718z" fill-rule="evenodd" />
+        </svg>
+        <span class="uppy-Dashboard-poweredByUppy">Uppy</span>
+      </a>
+    )
+  }
+
+  renderHiddenFileInput () {
+    return (
+      <input class="uppy-Dashboard-input"
+        hidden
+        aria-hidden="true"
+        tabindex={-1}
+        type="file"
+        name="files[]"
+        multiple={this.props.maxNumberOfFiles !== 1}
+        onchange={this.handleFileInputChange}
+        accept={this.props.allowedFileTypes}
+        ref={(ref) => { this.fileInput = ref }} />
+    )
+  }
 
-    // empty value="" on file input, so that the input is cleared after a file is selected,
-    // because Uppy will be handling the upload and so we can select same file
-    // after removing — otherwise browser thinks it’s already selected
+  renderDropPasteBrowseTagline () {
+    const browse =
+      <button type="button"
+        class="uppy-Dashboard-browse"
+        onclick={this.triggerFileInputClick}>
+        {this.props.i18n('browse')}
+      </button>
+
+    return (
+      <div class="uppy-Dashboard-dropFilesTitle">
+        {this.props.acquirers.length === 0
+          ? this.props.i18nArray('dropPaste', { browse })
+          : this.props.i18nArray('dropPasteImport', { browse })
+        }
+      </div>
+    )
+  }
+
+  renderMyDeviceAcquirer () {
+    return (
+      <div class="uppy-DashboardTab" role="presentation">
+        <button type="button"
+          class="uppy-DashboardTab-btn"
+          role="tab"
+          tabindex={0}
+          onclick={this.triggerFileInputClick}>
+          {localIcon()}
+          <div class="uppy-DashboardTab-name">{this.props.i18n('myDevice')}</div>
+        </button>
+      </div>
+    )
+  }
+
+  renderAcquirer (acquirer) {
+    return (
+      <div class="uppy-DashboardTab" role="presentation">
+        <button type="button"
+          class="uppy-DashboardTab-btn"
+          role="tab"
+          tabindex={0}
+          aria-controls={`uppy-DashboardContent-panel--${acquirer.id}`}
+          aria-selected={this.props.activePickerPanel.id === acquirer.id}
+          onclick={() => this.props.showPanel(acquirer.id)}>
+          {acquirer.icon()}
+          <div class="uppy-DashboardTab-name">{acquirer.name}</div>
+        </button>
+      </div>
+    )
+  }
+
+  render () {
     return (
       <div class="uppy-DashboardAddFiles">
+        {this.renderHiddenFileInput()}
         <div class="uppy-DashboardTabs">
-          <ActionBrowseTagline
-            acquirers={this.props.acquirers}
-            handleInputChange={this.props.handleInputChange}
-            i18n={this.props.i18n}
-            i18nArray={this.props.i18nArray}
-            allowedFileTypes={this.props.allowedFileTypes}
-            maxNumberOfFiles={this.props.maxNumberOfFiles}
-          />
-          <div class="uppy-DashboardTabs-list" role="tablist">
-            <div class="uppy-DashboardTab" role="presentation">
-              <button type="button"
-                class="uppy-DashboardTab-btn"
-                role="tab"
-                tabindex={0}
-                onclick={this.handleClick}>
-                {localIcon()}
-                <div class="uppy-DashboardTab-name">{this.props.i18n('myDevice')}</div>
-              </button>
-              <input class="uppy-Dashboard-input"
-                hidden
-                aria-hidden="true"
-                tabindex={-1}
-                type="file"
-                name="files[]"
-                multiple={this.props.maxNumberOfFiles !== 1}
-                accept={this.props.allowedFileTypes}
-                onchange={this.props.handleInputChange}
-                value=""
-                ref={(input) => { this.input = input }} />
+          {this.renderDropPasteBrowseTagline()}
+          {
+            this.props.acquirers.length > 0 &&
+            <div class="uppy-DashboardTabs-list" role="tablist">
+              {this.renderMyDeviceAcquirer()}
+              {this.props.acquirers.map((acquirer) =>
+                this.renderAcquirer(acquirer)
+              )}
             </div>
-            {this.props.acquirers.map((target) => {
-              return <div class="uppy-DashboardTab" role="presentation">
-                <button class="uppy-DashboardTab-btn"
-                  type="button"
-                  role="tab"
-                  tabindex={0}
-                  aria-controls={`uppy-DashboardContent-panel--${target.id}`}
-                  aria-selected={this.props.activePickerPanel.id === target.id}
-                  onclick={() => this.props.showPanel(target.id)}>
-                  {target.icon()}
-                  <div class="uppy-DashboardTab-name">{target.name}</div>
-                </button>
-              </div>
-            })}
-          </div>
+          }
         </div>
         <div class="uppy-DashboardAddFiles-info">
           { this.props.note && <div class="uppy-Dashboard-note">{this.props.note}</div> }
-          { this.props.proudlyDisplayPoweredByUppy && poweredByUppy(this.props) }
+          { this.props.proudlyDisplayPoweredByUppy && this.renderPoweredByUppy(this.props) }
         </div>
       </div>
     )

+ 11 - 7
packages/@uppy/drag-drop/src/index.js

@@ -88,10 +88,10 @@ module.exports = class DragDrop extends Plugin {
     })
   }
 
-  handleInputChange (ev) {
+  handleInputChange (event) {
     this.uppy.log('[DragDrop] Files selected through input')
 
-    const files = toArray(ev.target.files)
+    const files = toArray(event.target.files)
 
     files.forEach((file) => {
       try {
@@ -105,6 +105,14 @@ module.exports = class DragDrop extends Plugin {
         // Nothing, restriction errors handled in Core
       }
     })
+
+    // We clear the input after a file is selected, because otherwise
+    // change event is not fired in Chrome and Safari when a file
+    // with the same name is selected.
+    // ___Why not use value="" on <input/> instead?
+    //    Because if we use that method of clearing the input,
+    //    Chrome will not trigger change if we drop the same file twice (Issue #768).
+    event.target.value = null
   }
 
   render (state) {
@@ -140,11 +148,7 @@ module.exports = class DragDrop extends Plugin {
               name={this.opts.inputName}
               multiple={restrictions.maxNumberOfFiles !== 1}
               accept={restrictions.allowedFileTypes}
-              ref={(input) => {
-                this.input = input
-              }}
-              onchange={this.handleInputChange}
-              value="" />
+              onchange={this.handleInputChange} />
             {this.i18nArray('dropHereOr', {
               browse: <span class="uppy-DragDrop-dragText">{this.i18n('browse')}</span>
             })}

+ 29 - 21
packages/@uppy/file-input/src/index.js

@@ -36,10 +36,10 @@ module.exports = class FileInput extends Plugin {
     this.handleClick = this.handleClick.bind(this)
   }
 
-  handleInputChange (ev) {
+  handleInputChange (event) {
     this.uppy.log('[FileInput] Something selected through input...')
 
-    const files = toArray(ev.target.files)
+    const files = toArray(event.target.files)
 
     files.forEach((file) => {
       try {
@@ -53,6 +53,14 @@ module.exports = class FileInput extends Plugin {
         // Nothing, restriction errors handled in Core
       }
     })
+
+    // We clear the input after a file is selected, because otherwise
+    // change event is not fired in Chrome and Safari when a file
+    // with the same name is selected.
+    // ___Why not use value="" on <input/> instead?
+    //    Because if we use that method of clearing the input,
+    //    Chrome will not trigger change if we drop the same file twice (Issue #768).
+    event.target.value = null
   }
 
   handleClick (ev) {
@@ -73,25 +81,25 @@ module.exports = class FileInput extends Plugin {
     const restrictions = this.uppy.opts.restrictions
     const accept = restrictions.allowedFileTypes ? restrictions.allowedFileTypes.join(',') : null
 
-    // empty value="" on file input, so that the input is cleared after a file is selected,
-    // because Uppy will be handling the upload and so we can select same file
-    // after removing — otherwise browser thinks it’s already selected
-    return <div class="uppy-Root uppy-FileInput-container">
-      <input class="uppy-FileInput-input"
-        style={this.opts.pretty && hiddenInputStyle}
-        type="file"
-        name={this.opts.inputName}
-        onchange={this.handleInputChange}
-        multiple={restrictions.maxNumberOfFiles !== 1}
-        accept={accept}
-        ref={(input) => { this.input = input }}
-        value="" />
-      {this.opts.pretty &&
-        <button class="uppy-FileInput-btn" type="button" onclick={this.handleClick}>
-          {this.i18n('chooseFiles')}
-        </button>
-      }
-    </div>
+    return (
+      <div class="uppy-Root uppy-FileInput-container">
+        <input class="uppy-FileInput-input"
+          style={this.opts.pretty && hiddenInputStyle}
+          type="file"
+          name={this.opts.inputName}
+          onchange={this.handleInputChange}
+          multiple={restrictions.maxNumberOfFiles !== 1}
+          accept={accept}
+          ref={(input) => { this.input = input }} />
+        {this.opts.pretty &&
+          <button class="uppy-FileInput-btn"
+            type="button"
+            onclick={this.handleClick}>
+            {this.i18n('chooseFiles')}
+          </button>
+        }
+      </div>
+    )
   }
 
   install () {