API Docs for: 0.25.0
Show:

File: addon/components/file-chooser/component.js

import Ember from 'ember';
import layout from './template';

/**
 * @module ember-osf
 * @submodule components
 */

/**
 * This component lets the user choose a list of files from their computer, by
 * drag-and-drop, a file browser, or whatever method the developer wants.
 *
 * Exposed to parent context (bindable attributes)
 *  - `files`: mutable list of chosen File objects
 *  - `multiple`: boolean (default `true`), when `false`, only keeps the most
 *          recently chosen File in the list
 *  - `onChoose`: callback function called each time a file is added, with the
 *          new File object as the only argument
 *
 * Exposed to block context
 *  - `this`: the component object itself, so the block can invoke actions
 *      example:
 *      ```handlebars
 *          {{#file-chooser files=fileList as |component|}}
 *              {{input type='file'
 *                  change=(action 'onFileInputChange' target=component)}}
 *          {{/file-chooser}}
 *      ```
 *
 * Actions
 *  - `onFileInputChange`: handle the `change` event on a file input
 *  - `onChooseFile`: add a file to the chosen list
 *
 * Styling
 *  - This component's element has the `drop-zone` class
 *  - While the user is holding dragged files over this component, it
 *    has the `drop-zone-ready` class
 *
 * @class file-chooser component
 */
export default Ember.Component.extend({
    layout,
    i18n: Ember.inject.service(),
    classNames: ['drop-zone'],
    classNameBindings: ['dropZoneReady'],
    dropZoneReady: false,
    multiple: true,

    dragOver(event) {
        if (event.dataTransfer.types.indexOf('Files') > -1) {
            this.set('dropZoneReady', true);
            event.dataTransfer.dropEffect = 'move';
            return false;
        } else {
            event.dataTransfer.dropEffect = 'none';
        }
    },

    dragLeave(event) {
        this.set('dropZoneReady', false);
        event.dataTransfer.dropEffect = '';
    },

    drop(event) {
        event.preventDefault();
        this.set('dropZoneReady', false);
        for (let i = 0; i < event.dataTransfer.files.length; i++) {
            let file = event.dataTransfer.files[i];
            this._chooseIfFile(file);
        }
    },

    actions: {
        onFileInputChange(event) {
            for (let i = 0; i < event.target.files.length; i++) {
                let file = event.target.files[i];
                this.send('onChooseFile', file);
            }
        },

        onChooseFile(file) {
            let files = this.get('files');
            if (typeof files === 'undefined') {
                this.set('files', Ember.A());
                files = this.get('files');
            }
            if (this.get('multiple')) {
                files.pushObject(file);
            } else {
                this.set('files', Ember.A([file]));
            }

            let callback = this.get('onChoose');
            if (callback) {
                callback(file);
            }
        }
    },

    _chooseIfFile(file) {
        // HACK: There's not a cross-browser way to see the contents of
        // dragged-and-dropped directories, but there's also not a good way to
        // tell whether a given File object is a directory. Hence, this:
        let p = new Ember.RSVP.Promise(function(resolve, reject) {
            let reader = new FileReader();
            reader.onload = () => resolve(); // it's a file
            reader.onerror = () => reject(); // it's a directory or something
            reader.readAsText(file.slice(0, 5));
        });
        p.then(() => this.send('onChooseFile', file));
        p.catch(() => this.set('errorMessage',
            `Cannot upload directories (${file.name})`));
    }
});