2
0
Эх сурвалжийг харах

impl replace inline-attachment with Dropzone.js

impl isUploading state
Yuki Takei 8 жил өмнө
parent
commit
d147527d10

+ 44 - 23
resource/css/_form.scss

@@ -80,19 +80,6 @@
     }
 
     // for Dropzone
-    .dropzone-overlay {
-      // layout
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      // style
-      font-size: 2em;
-      margin: 0 15px;
-
-      // > * {
-      //   pointer-events: none;
-      // }
-    }
     .dropzone {
       @mixin insertFontAwesome($code) {
         &:before {
@@ -102,17 +89,45 @@
         }
       }
 
+      // default layout and style
+      .dropzone-overlay {
+        // layout
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        // style
+        margin: 0 15px;
+      }
+      .dropzone-overlay-content {
+        font-size: 2em;
+        padding: 0.2em;
+        border-radius: 5px;
+      }
+
       // unuploadable or rejected
       &.dropzone-unuploadable, &.dropzone-rejected {
         .dropzone-overlay {
           background: rgba(200,200,200,0.8);
+        }
+        .dropzone-overlay-content {
+          color: #444;
+        }
+      }
+      // uploading
+      &.dropzone-uploading {
+        .dropzone-overlay {
+          background: rgba(255,255,255,0.5);
+        }
+        .dropzone-overlay-content {
+          padding: 0.3em;
+          background: rgba(200,200,200,0.5);
           color: #444;
         }
       }
 
       // unuploadable
       &.dropzone-unuploadable {
-        .dropzone-overlay {
+        .dropzone-overlay-content {
           // insert content
           @include insertFontAwesome("\f06a ");  // fa-exclamation-circle
           &:after {
@@ -123,17 +138,23 @@
       // uploadable
       &.dropzone-uploadable {
         // accepted
-        &.dropzone-accepted:not(.dropzone-rejected) .dropzone-overlay {
-          color: #999;
-          border: 4px dashed #ccc;
-          // insert content
-          @include insertFontAwesome("\f093");  // fa-upload
-          &:after {
-            content: "Drop here to upload";
+        &.dropzone-accepted:not(.dropzone-rejected) {
+          .dropzone-overlay {
+            border: 4px dashed #ccc;
+          }
+          .dropzone-overlay-content {
+            // insert content
+            @include insertFontAwesome("\f093");  // fa-upload
+            &:after {
+              content: "Drop here to upload";
+            }
+            // style
+            color: #666;
+            background: rgba(200,200,200,0.8);
           }
         }
         // file type mismatch
-        &.dropzone-rejected:not(.dropzone-uploadablefile) .dropzone-overlay {
+        &.dropzone-rejected:not(.dropzone-uploadablefile) .dropzone-overlay-content {
           // insert content
           @include insertFontAwesome("\f03e");  // fa-picture-o
           &:after {
@@ -141,7 +162,7 @@
           }
         }
         // multiple files
-        &.dropzone-accepted.dropzone-rejected .dropzone-overlay {
+        &.dropzone-accepted.dropzone-rejected .dropzone-overlay-content {
           // insert content
           @include insertFontAwesome("\f071");  // fa-fa-exclamation-triangle
           &:after {

+ 5 - 1
resource/js/components/PageEditor.js

@@ -150,7 +150,11 @@ export default class PageEditor extends React.Component {
           this.pageSavedHandler(res.page);
         }
       })
-      .catch(this.apiErrorHandler);
+      .catch(this.apiErrorHandler)
+      // finally
+      .then(() => {
+        this.refs.editor.terminateUploadingState();
+      });
   }
 
   /**

+ 40 - 22
resource/js/components/PageEditor/Editor.js

@@ -35,6 +35,7 @@ export default class Editor extends React.Component {
     this.state = {
       value: this.props.value,
       dropzoneActive: false,
+      isUploading: false,
     };
 
     this.getCodeMirror = this.getCodeMirror.bind(this);
@@ -46,7 +47,7 @@ export default class Editor extends React.Component {
     this.onDragLeave = this.onDragLeave.bind(this);
     this.onDrop = this.onDrop.bind(this);
 
-    this.renderOverlayMessage = this.renderOverlayMessage.bind(this);
+    this.renderOverlay = this.renderOverlay.bind(this);
   }
 
   componentDidMount() {
@@ -80,6 +81,16 @@ export default class Editor extends React.Component {
     editor.setCursor({line: line-1});   // leave 'ch' field as null/undefined to indicate the end of line
   }
 
+  /**
+   * remove overlay and set isUploading to false
+   */
+  terminateUploadingState() {
+    this.setState({
+      dropzoneActive: false,
+      isUploading: false,
+    });
+  }
+
   /**
    * insert text
    * @param {string} text
@@ -115,32 +126,26 @@ export default class Editor extends React.Component {
       return;
     }
 
-    this.setState({
-      dropzoneActive: true
-    });
+    this.setState({ dropzoneActive: true });
   }
 
   onDragLeave() {
-    this.setState({
-      dropzoneActive: false
-    });
+    this.setState({ dropzoneActive: false });
   }
 
   onDrop(accepted, rejected) {
-    // if (!this.props.isUploadable) {
-    //   return;
-    // }
-
-    // this.setState({
-    //   dropzoneActive: false
-    // });
-
-    // // TODO abort multi files
+    // rejected
+    if (accepted.length != 1) { // length should be 0 or 1 because `multiple={false}` is set
+      this.setState({ dropzoneActive: false });
+      return;
+    }
 
-    // this.dispatchUpload(files[0]);
+    const file = accepted[0];
+    this.dispatchUpload(file);
+    this.setState({ isUploading: true });
   }
 
-  renderOverlayMessage() {
+  renderOverlay() {
     const overlayStyle = {
       position: 'absolute',
       zIndex: 1060, // FIXME: required because .content-main.on-edit has 'z-index:1050'
@@ -151,7 +156,15 @@ export default class Editor extends React.Component {
     };
 
     return (
-      <div style={overlayStyle} className="dropzone-overlay"></div>
+      <div style={overlayStyle} className="dropzone-overlay">
+        {this.state.isUploading &&
+          <span className="dropzone-overlay-content">
+            <i className="fa fa-spinner fa-pulse fa-fw"></i>
+            <span className="sr-only">Uploading...</span>
+          </span>
+        }
+        {!this.state.isUploading && <span className="dropzone-overlay-content"></span>}
+      </div>
     );
   }
 
@@ -160,7 +173,7 @@ export default class Editor extends React.Component {
       height: '100%'
     }
 
-    let accept = null;
+    let accept = 'null';  // reject all
     let className = 'dropzone';
     if (!this.props.isUploadable) {
       className += ' dropzone-unuploadable';
@@ -171,10 +184,15 @@ export default class Editor extends React.Component {
 
       if (this.props.isUploadableFile) {
         className += ' dropzone-uploadablefile';
-        accept = '';
+        accept = '';  // allow all
       }
     }
 
+    // uploading
+    if (this.state.isUploading) {
+      className += ' dropzone-uploading';
+    }
+
     return (
       <Dropzone
         disableClick
@@ -188,7 +206,7 @@ export default class Editor extends React.Component {
         onDragLeave={this.onDragLeave}
         onDrop={this.onDrop}
       >
-        { this.state.dropzoneActive && this.renderOverlayMessage() }
+        { this.state.dropzoneActive && this.renderOverlay() }
 
         <ReactCodeMirror
           ref="cm"