Pārlūkot izejas kodu

Merge pull request #814 from transloadit/improvement/restrictions-accept-multiple

Pass allowedFileTypes and maxNumberOfFiles to input[type=file]
Artur Paikin 7 gadi atpakaļ
vecāks
revīzija
c8434606da

+ 18 - 6
src/core/Core.js

@@ -43,10 +43,10 @@ class Uppy {
       autoProceed: true,
       debug: false,
       restrictions: {
-        maxFileSize: false,
-        maxNumberOfFiles: false,
-        minNumberOfFiles: false,
-        allowedFileTypes: false
+        maxFileSize: null,
+        maxNumberOfFiles: null,
+        minNumberOfFiles: null,
+        allowedFileTypes: null
       },
       meta: {},
       onBeforeFileAdded: (currentFile, files) => currentFile,
@@ -322,8 +322,20 @@ class Uppy {
 
     if (allowedFileTypes) {
       const isCorrectFileType = allowedFileTypes.filter((type) => {
-        if (!file.type) return false
-        return match(file.type, type)
+        // if (!file.type) return false
+
+        // is this is a mime-type
+        if (type.indexOf('/') > -1) {
+          if (!file.type) return false
+          return match(file.type, type)
+        }
+
+        // otherwise this is likely an extension
+        if (type[0] === '.') {
+          if (file.extension === type.substr(1)) {
+            return file.extension
+          }
+        }
       }).length > 0
 
       if (!isCorrectFileType) {

+ 24 - 2
src/core/Core.test.js

@@ -1042,7 +1042,7 @@ describe('src/Core', () => {
 
     xit('should enforce the minNumberOfFiles rule', () => {})
 
-    it('should enfore the allowedFileTypes rule', () => {
+    it('should enforce the allowedFileTypes rule', () => {
       const core = new Core({
         autoProceed: false,
         restrictions: {
@@ -1064,6 +1064,28 @@ describe('src/Core', () => {
       }
     })
 
+    it('should enforce the allowedFileTypes rule with file extensions', () => {
+      const core = new Core({
+        autoProceed: false,
+        restrictions: {
+          allowedFileTypes: ['.gif', '.jpg', '.jpeg']
+        }
+      })
+
+      try {
+        core.addFile({
+          source: 'jest',
+          name: 'foo2.png',
+          type: '',
+          data: new File([sampleImage], { type: 'image/jpeg' })
+        })
+        throw new Error('should have thrown')
+      } catch (err) {
+        expect(err).toMatchObject(new Error('You can only upload: .gif, .jpg, .jpeg'))
+        expect(core.state.info.message).toEqual('You can only upload: .gif, .jpg, .jpeg')
+      }
+    })
+
     it('should enforce the maxFileSize rule', () => {
       const core = new Core({
         autoProceed: false,
@@ -1295,7 +1317,7 @@ describe('src/Core', () => {
       })
 
       expect(core.opts.restrictions.maxNumberOfFiles).toBe(3)
-      expect(core.opts.restrictions.minNumberOfFiles).toBe(false)
+      expect(core.opts.restrictions.minNumberOfFiles).toBe(null)
     })
   })
 })

+ 2 - 1
src/plugins/Dashboard/ActionBrowseTagline.js

@@ -28,8 +28,9 @@ class ActionBrowseTagline extends Component {
           tabindex="-1"
           type="file"
           name="files[]"
-          multiple="true"
+          multiple={this.props.maxNumberOfFiles !== 1}
           onchange={this.props.handleInputChange}
+          accept={this.props.allowedFileTypes}
           value=""
           ref={(input) => {
             this.input = input

+ 3 - 1
src/plugins/Dashboard/FileList.js

@@ -18,7 +18,9 @@ module.exports = (props) => {
           {h(ActionBrowseTagline, {
             acquirers: props.acquirers,
             handleInputChange: props.handleInputChange,
-            i18n: props.i18n
+            i18n: props.i18n,
+            allowedFileTypes: props.allowedFileTypes,
+            maxNumberOfFiles: props.maxNumberOfFiles
           })}
         </div>
         { props.note && <div class="uppy-Dashboard-note">{props.note}</div> }

+ 2 - 1
src/plugins/Dashboard/Tabs.js

@@ -49,7 +49,8 @@ class Tabs extends Component {
             tabindex="-1"
             type="file"
             name="files[]"
-            multiple="true"
+            multiple={this.props.maxNumberOfFiles !== 1}
+            accept={this.props.allowedFileTypes}
             onchange={this.props.handleInputChange}
             value=""
             ref={(input) => { this.input = input }} />

+ 3 - 1
src/plugins/Dashboard/index.js

@@ -486,7 +486,9 @@ module.exports = class Dashboard extends Plugin {
       proudlyDisplayPoweredByUppy: this.opts.proudlyDisplayPoweredByUppy,
       currentWidth: pluginState.containerWidth,
       isWide: pluginState.containerWidth > 400,
-      isTargetDOMEl: this.isTargetDOMEl
+      isTargetDOMEl: this.isTargetDOMEl,
+      allowedFileTypes: this.uppy.opts.restrictions.allowedFileTypes,
+      maxNumberOfFiles: this.uppy.opts.restrictions.maxNumberOfFiles
     })
   }
 

+ 18 - 5
src/plugins/DragDrop/index.js

@@ -26,7 +26,6 @@ module.exports = class DragDrop extends Plugin {
     const defaultOpts = {
       target: null,
       inputName: 'files[]',
-      allowMultipleFiles: true,
       width: '100%',
       height: '100%',
       note: null,
@@ -104,11 +103,21 @@ module.exports = class DragDrop extends Plugin {
   }
 
   render (state) {
-    const DragDropClass = `uppy-Root uppy-DragDrop-container ${this.isDragDropSupported ? 'is-dragdrop-supported' : ''}`
+    /* http://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/ */
+    const hiddenInputStyle = {
+      width: '0.1px',
+      height: '0.1px',
+      opacity: 0,
+      overflow: 'hidden',
+      position: 'absolute',
+      zIndex: -1
+    }
+    const DragDropClass = `uppy-Root uppy-DragDrop-container ${this.isDragDropSupported ? 'uppy-DragDrop--is-dragdrop-supported' : ''}`
     const DragDropStyle = {
       width: this.opts.width,
       height: this.opts.height
     }
+    const restrictions = this.uppy.opts.restrictions
 
     // 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
@@ -120,11 +129,15 @@ module.exports = class DragDrop extends Plugin {
             <path d="M11 10V0H5v10H2l6 6 6-6h-3zm0 0" fill-rule="evenodd" />
           </svg>
           <label class="uppy-DragDrop-label">
-            <input class="uppy-DragDrop-input"
+            <input style={hiddenInputStyle}
+              class="uppy-DragDrop-input"
               type="file"
               name={this.opts.inputName}
-              multiple={this.opts.allowMultipleFiles}
-              ref={(input) => { this.input = input }}
+              multiple={restrictions.maxNumberOfFiles !== 1}
+              accept={restrictions.allowedFileTypes}
+              ref={(input) => {
+                this.input = input
+              }}
               onchange={this.handleInputChange}
               value="" />
             {this.i18n('dropHereOr')} <span class="uppy-DragDrop-dragText">{this.i18n('browse')}</span>

+ 5 - 2
src/plugins/FileInput.js

@@ -19,7 +19,6 @@ module.exports = class FileInput extends Plugin {
     // Default options
     const defaultOptions = {
       target: null,
-      allowMultipleFiles: true,
       pretty: true,
       inputName: 'files[]',
       locale: defaultLocale
@@ -60,6 +59,7 @@ module.exports = class FileInput extends Plugin {
   }
 
   render (state) {
+    /* http://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/ */
     const hiddenInputStyle = {
       width: '0.1px',
       height: '0.1px',
@@ -69,6 +69,8 @@ module.exports = class FileInput extends Plugin {
       zIndex: -1
     }
 
+    const restrictions = this.uppy.opts.restrictions
+
     // 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
@@ -78,7 +80,8 @@ module.exports = class FileInput extends Plugin {
         type="file"
         name={this.opts.inputName}
         onchange={this.handleInputChange}
-        multiple={this.opts.allowMultipleFiles}
+        multiple={restrictions.maxNumberOfFiles !== 1}
+        accept={restrictions.allowedFileTypes}
         ref={(input) => { this.input = input }}
         value="" />
       {this.opts.pretty &&

+ 2 - 12
src/scss/_dragdrop.scss

@@ -23,7 +23,7 @@
     margin-bottom: 17px;
   }
   
-    .uppy-DragDrop-container.is-dragdrop-supported {
+    .uppy-DragDrop--is-dragdrop-supported {
       border: 2px dashed;
       border-color: lighten($color-gray, 10%);
     }
@@ -41,16 +41,6 @@
       fill: $color-gray;
     }
   
-  /* http://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/ */
-  .uppy-DragDrop-input {
-    width: 0.1px;
-    height: 0.1px;
-    opacity: 0;
-    overflow: hidden;
-    position: absolute;
-    z-index: -1;
-  }
-  
   .uppy-DragDrop-label {
     display: block;
     cursor: pointer;
@@ -67,4 +57,4 @@
     color: $color-cornflower-blue;
   }
 
-// }
+// }

+ 2 - 5
website/src/docs/dragdrop.md

@@ -16,7 +16,6 @@ uppy.use(DragDrop, {
   target: null,
   width: '100%',
   height: '100%',
-  allowMultipleFiles: true,
   note: null,
   locale: {
     strings: {
@@ -27,6 +26,8 @@ uppy.use(DragDrop, {
 })
 ```
 
+> Note that certain [restrictions set in Uppy’s main options](/docs/uppy#restrictions), namely `maxNumberOfFiles` and `allowedFileTypes`, affect the system file picker dialog. If `maxNumberOfFiles: 1`, users will only be able to select one file, and `allowedFileTypes: ['video/*', '.gif']` means only videos or gifs (files with `.gif` extension) will be selectable.
+
 ### `target: null`
 
 DOM element, CSS selector, or plugin to place the drag and drop area into.
@@ -39,10 +40,6 @@ Drag and drop area width, set in inline CSS, so feel free to use percentage, pix
 
 Drag and drop area height, set in inline CSS, so feel free to use percentage, pixels or other values that you like.
 
-### `allowMultipleFiles: true`
-
-Whether to allow user to select multiple files at once via the system file dialog.
-
 ### `note: null`
 
 Optionally specify a string of text that explains something about the upload for the user. This is a place to explain `restrictions` that are put in place. For example: `'Images and video only, 2–3 files, up to 1 MB'`.

+ 2 - 5
website/src/docs/fileinput.md

@@ -14,7 +14,6 @@ permalink: docs/fileinput/
 ```js
 uppy.use(FileInput, {
   target: null,
-  allowMultipleFiles: true,
   pretty: true,
   inputName: 'files[]',
   locale: {
@@ -25,14 +24,12 @@ uppy.use(FileInput, {
 })
 ```
 
+> Note that certain [restrictions set in Uppy’s main options](/docs/uppy#restrictions), namely `maxNumberOfFiles` and `allowedFileTypes`, affect the system file picker dialog. If `maxNumberOfFiles: 1`, users will only be able to select one file, and `allowedFileTypes: ['video/*', '.gif']` means only videos or gifs (files with `.gif` extension) will be selectable.
+
 ### `target: null`
 
 DOM element, CSS selector, or plugin to mount the file input into.
 
-### `allowMultipleFiles: true`
-
-Whether to allow the user to select multiple files at once.
-
 ### `pretty: true`
 
 When true, display a styled button (see [example](/examples/xhrupload)) that, when clicked, opens the file selector UI. When false, a plain old browser `<input type="file">` element is shown.

+ 12 - 8
website/src/docs/uppy.md

@@ -15,10 +15,10 @@ const uppy = Uppy({
   autoProceed: true,
   debug: false,
   restrictions: {
-    maxFileSize: false,
-    maxNumberOfFiles: false,
-    minNumberOfFiles: false,
-    allowedFileTypes: false
+    maxFileSize: null,
+    maxNumberOfFiles: null,
+    minNumberOfFiles: null,
+    allowedFileTypes: null
   },
   meta: {},
   onBeforeFileAdded: (currentFile, files) => currentFile,
@@ -52,10 +52,14 @@ Optionally provide rules and conditions for which files can be selected.
 
 **Parameters**
 
-- `maxFileSize` *number*
-- `maxNumberOfFiles` *number*
-- `minNumberOfFiles` *number*
-- `allowedFileTypes` *array* of wildcards or exact mime types, like `image/*`
+- `maxFileSize` *null | number*
+- `maxNumberOfFiles` *null | number*
+- `minNumberOfFiles` *null | number*
+- `allowedFileTypes` *null | array* of wildcards `image/*`, exact mime types `image/jpeg`, or file extensions `.jpg`: `['image/*', '.jpg', '.jpeg', '.png', '.gif']`
+
+`maxNumberOfFiles` affects the number of files user is able to select via the system file dialog in UI plugins like `DragDrop`, `FileInput` and `Dashboard`: when set to `1` they will only be able to select a single file, otherwise, when `null` or other number, they will be able to select multiple files.
+
+`allowedFileTypes` gets passed to the system file dialog via [`<input>`]((https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Limiting_accepted_file_types))’s accept attribute, so only files matching these types will be selectable.
 
 ### `meta: {}`