Yuki Takei 8 лет назад
Родитель
Сommit
9e74b7a10b

+ 38 - 2
resource/js/components/PageEditor/Editor.js

@@ -43,6 +43,8 @@ export default class Editor extends React.Component {
     this.forceToFocus = this.forceToFocus.bind(this);
     this.dispatchSave = this.dispatchSave.bind(this);
 
+    this.onPaste = this.onPaste.bind(this);
+
     this.onDragEnterForCM = this.onDragEnterForCM.bind(this);
     this.onDragLeave = this.onDragLeave.bind(this);
     this.onDrop = this.onDrop.bind(this);
@@ -118,6 +120,40 @@ export default class Editor extends React.Component {
     }
   }
 
+  /**
+   * CodeMirror paste event handler
+   * see: https://codemirror.net/doc/manual.html#events
+   * @param {any} editor An editor instance of CodeMirror
+   * @param {any} event
+   */
+  onPaste(editor, event) {
+    const types = event.clipboardData.types;
+
+    // text
+    if (types.includes('text/plain')) {
+      pasteHelper.pasteText(editor, event);
+    }
+    // files
+    else if (types.includes('Files')) {
+      const dropzone = this.refs.dropzone;
+      const items = event.clipboardData.items || event.clipboardData.files || [];
+
+      // abort if length is not 1
+      if (items.length != 1) {
+        return;
+      }
+
+      const file = items[0].getAsFile();
+      // check type and size
+      if (pasteHelper.fileAccepted(file, dropzone.props.accept) &&
+          pasteHelper.fileMatchSize(file, dropzone.props.maxSize, dropzone.props.minSize)) {
+
+        this.dispatchUpload(file);
+        this.setState({ isUploading: true });
+      }
+    }
+  }
+
   onDragEnterForCM(editor, event) {
     const dataTransfer = event.dataTransfer;
 
@@ -219,7 +255,7 @@ export default class Editor extends React.Component {
             ref="cm"
             editorDidMount={(editor) => {
               // add event handlers
-              editor.on('paste', pasteHelper.pasteHandler);
+              editor.on('paste', this.onPaste);
             }}
             value={this.state.value}
             options={{
@@ -265,7 +301,7 @@ export default class Editor extends React.Component {
           <i className="fa fa-paperclip" aria-hidden="true"></i>&nbsp;
           Attach files by dragging &amp; dropping,&nbsp;
           <span className="btn-link">selecting them</span>,&nbsp;
-          or (TBD) pasting from the clipboard.
+          or pasting from the clipboard.
         </button>
       </div>
     )

+ 26 - 14
resource/js/components/PageEditor/PasteHelper.js

@@ -1,26 +1,15 @@
+import accepts from 'attr-accept'
+
 class PasteHelper {
 
   constructor() {
     // https://regex101.com/r/7BN2fR/3
     this.indentAndMarkPattern = /^([ \t]*)(?:>|\-|\+|\*|\d+\.) /;
 
-    this.pasteHandler = this.pasteHandler.bind(this);
     this.pasteText = this.pasteText.bind(this);
     this.adjustPastedData = this.adjustPastedData.bind(this);
   }
 
-  /**
-   * CodeMirror paste event handler
-   * see: https://codemirror.net/doc/manual.html#events
-   * @param {any} editor An editor instance of CodeMirror
-   * @param {any} event
-   */
-  pasteHandler(editor, event) {
-    if (event.clipboardData.types.includes('text/plain') > -1) {
-      this.pasteText(editor, event);
-    }
-  }
-
   /**
    * paste text
    * @param {any} editor An editor instance of CodeMirror
@@ -28,7 +17,7 @@ class PasteHelper {
    */
   pasteText(editor, event) {
     // get data in clipboard
-    let text = event.clipboardData.getData('text/plain');
+    const text = event.clipboardData.getData('text/plain');
 
     if (text.length == 0) { return; }
 
@@ -120,6 +109,29 @@ class PasteHelper {
     return isListful;
   }
 
+
+  // Firefox versions prior to 53 return a bogus MIME type for every file drag, so dragovers with
+  /**
+   * transplanted from react-dropzone
+   * @see https://github.com/react-dropzone/react-dropzone/blob/master/src/utils/index.js
+   *
+   * @param {*} file
+   * @param {*} accept
+   */
+  fileAccepted(file, accept) {
+    return file.type === 'application/x-moz-file' || accepts(file, accept)
+  }
+  /**
+   * transplanted from react-dropzone
+   * @see https://github.com/react-dropzone/react-dropzone/blob/master/src/utils/index.js
+   *
+   * @param {*} file
+   * @param {number} maxSize
+   * @param {number} minSize
+   */
+  fileMatchSize(file, maxSize, minSize) {
+    return file.size <= maxSize && file.size >= minSize
+  }
 }
 
 // singleton pattern