Merge pull request #879 from strapi/improve-wys-upload

Improve file upload in WYSIWYG
This commit is contained in:
Jim LAURIE 2018-04-02 13:29:46 +02:00 committed by GitHub
commit 5796d96ecc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 146 additions and 37 deletions

View File

@ -110,6 +110,8 @@
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
"components.WysiwygBottomControls.charactersIndicators": "characters",
"components.WysiwygBottomControls.uploadFiles": "Attach files by dragging & dropping, {browse}, or pasting from the clipboard.",
"components.WysiwygBottomControls.uploadFiles.browse": "selecting them",
"components.WysiwygBottomControls.fullscreen": "Expand",
"notification.error": "Ein Fehler ist aufgetreten",

View File

@ -116,6 +116,8 @@
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
"components.WysiwygBottomControls.charactersIndicators": "characters",
"components.WysiwygBottomControls.uploadFiles": "Attach files by dragging & dropping, {browse}, or pasting from the clipboard.",
"components.WysiwygBottomControls.uploadFiles.browse": "selecting them",
"components.WysiwygBottomControls.fullscreen": "Expand",
"notification.error": "An error occurred",

View File

@ -116,6 +116,8 @@
"components.Wysiwyg.ToggleMode.markdown": "Retour au markdown",
"components.Wysiwyg.ToggleMode.preview": "Voir la preview",
"components.WysiwygBottomControls.charactersIndicators": "charactères",
"components.WysiwygBottomControls.uploadFiles": "Ajouter des fichiers en les 'droppant', {browse}, ou en les collant depuis le presse-papier",
"components.WysiwygBottomControls.uploadFiles.browse": "en les selectionnant",
"components.WysiwygBottomControls.fullscreen": "Plein écran",
"notification.error": "Une erreur est survenue",

View File

@ -113,6 +113,8 @@
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
"components.WysiwygBottomControls.charactersIndicators": "characters",
"components.WysiwygBottomControls.uploadFiles": "Attach files by dragging & dropping, {browse}, or pasting from the clipboard.",
"components.WysiwygBottomControls.uploadFiles.browse": "selecting them",
"components.WysiwygBottomControls.fullscreen": "Expand",
"notification.error": "Wystąpił błąd",

View File

@ -114,6 +114,8 @@
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
"components.WysiwygBottomControls.charactersIndicators": "characters",
"components.WysiwygBottomControls.uploadFiles": "Attach files by dragging & dropping, {browse}, or pasting from the clipboard.",
"components.WysiwygBottomControls.uploadFiles.browse": "selecting them",
"components.WysiwygBottomControls.fullscreen": "Expand",
"notification.error": "Bir hata oluştu",

View File

@ -118,6 +118,8 @@
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
"components.WysiwygBottomControls.charactersIndicators": "characters",
"components.WysiwygBottomControls.uploadFiles": "Attach files by dragging & dropping, {browse}, or pasting from the clipboard.",
"components.WysiwygBottomControls.uploadFiles.browse": "selecting them",
"components.WysiwygBottomControls.fullscreen": "Expand",
"notification.error": "有錯誤發生了",

View File

@ -56,9 +56,9 @@
}
// NOTE: we might need this later
// span {
// white-space: pre-line;
// }
span {
white-space: pre-line;
}
}
h1+.editorParagraph{

View File

@ -369,28 +369,8 @@ class Wysiwyg extends React.Component {
return this.setState({ isDraging: false });
}
const { dataTransfer: { files } } = e;
const formData = new FormData();
formData.append('files', files[0]);
const headers = {
'X-Forwarded-Host': 'strapi',
};
return request('/upload', { method: 'POST', headers, body: formData }, false, false)
.then(response => {
const newContentState = this.createNewContentStateFromBlock(
createNewBlock(`![text](${response[0].url})`),
);
const newEditorState = EditorState.push(this.getEditorState(), newContentState);
this.setState({ editorState: newEditorState });
this.sendData(newEditorState);
})
.catch(err => {
console.log('error', err.response);
})
.finally(() => {
this.setState({ isDraging: false });
});
const files = e.dataTransfer ? e.dataTransfer.files : e.target.files;
return this.uploadFile(files);
};
handleKeyCommand = (command, editorState) => {
@ -410,6 +390,8 @@ class Wysiwyg extends React.Component {
return false;
};
handlePastedFiles = files => this.uploadFile(files);
handleReturn = (e, editorState) => {
const selection = editorState.getSelection();
const currentBlock = editorState.getCurrentContent().getBlockForKey(selection.getStartKey());
@ -474,6 +456,37 @@ class Wysiwyg extends React.Component {
});
};
uploadFile = (files) => {
const formData = new FormData();
formData.append('files', files[0]);
const headers = {
'X-Forwarded-Host': 'strapi',
};
const newContentState = this.createNewContentStateFromBlock(
createNewBlock(`![Uploading ${files[0].name}]()`),
);
const newEditorState = EditorState.push(this.getEditorState(), newContentState);
this.setState({ editorState: newEditorState });
return request('/upload', { method: 'POST', headers, body: formData }, false, false)
.then(response => {
const lastBlock = this.getEditorState().getCurrentContent().getLastBlock();
const newContentState = this.createNewContentStateFromBlock(
createNewBlock(`![text](${response[0].url})`, 'unstyled', lastBlock.getKey()),
);
const newEditorState = EditorState.push(this.getEditorState(), newContentState);
this.setState({ editorState: EditorState.moveFocusToEnd(newEditorState) });
this.sendData(newEditorState);
})
.catch(() => {
this.setState({ editorState: EditorState.undo(this.getEditorState()) });
})
.finally(() => {
this.setState({ isDraging: false });
});
}
componentDidCatch(error, info) {
console.log('err', error);
console.log('info', info);
@ -549,6 +562,7 @@ class Wysiwyg extends React.Component {
blockStyleFn={getBlockStyle}
editorState={editorState}
handleKeyCommand={this.handleKeyCommand}
handlePastedFiles={this.handlePastedFiles}
handleReturn={this.handleReturn}
keyBindingFn={this.mapKeyToEditorCommand}
onBlur={this.handleBlur}
@ -563,8 +577,9 @@ class Wysiwyg extends React.Component {
)}
{!isFullscreen && (
<WysiwygBottomControls
charactersNumber={this.getCharactersNumber()}
isPreviewMode={isPreviewMode}
onClick={this.toggleFullScreen}
onChange={this.handleDrop}
/>
)}
</div>

View File

@ -12,10 +12,12 @@ import { isEmpty } from 'lodash';
import WysiwygEditor from 'components/WysiwygEditor';
import converter from './converter';
import { findLinkEntities, findImageEntities } from './strategies';
import { findLinkEntities, findImageEntities, findVideoEntities } from './strategies';
import Image from './image';
import Link from './link';
import Video from './video';
import styles from './componentsStyles.scss';
function getBlockStyle(block) {
@ -61,6 +63,10 @@ class PreviewWysiwyg extends React.Component {
strategy: findImageEntities,
component: Image,
},
{
strategy: findVideoEntities,
component: Video,
},
]);
const blocksFromHTML = convertFromHTML(html);

View File

@ -8,8 +8,19 @@ function findLinkEntities(contentBlock, callback, contentState) {
function findImageEntities(contentBlock, callback, contentState) {
contentBlock.findEntityRanges(character => {
const entityKey = character.getEntity();
return entityKey !== null && contentState.getEntity(entityKey).getType() === 'IMAGE';
return entityKey !== null && contentState.getEntity(entityKey).getType() === 'IMAGE' && !isVideoType(contentState.getEntity(entityKey).getData().src);
}, callback);
}
export { findLinkEntities, findImageEntities };
function findVideoEntities(contentBlock, cb, contentState) {
contentBlock.findEntityRanges(character => {
const entityKey = character.getEntity();
return entityKey !== null && contentState.getEntity(entityKey).getType() === 'IMAGE' && isVideoType(contentState.getEntity(entityKey).getData().src);
}, cb);
}
const isVideoType = (fileName) => /\.(mp4|mpg|mpeg|mov|avi)$/i.test(fileName);
export { findLinkEntities, findImageEntities, findVideoEntities };

View File

@ -0,0 +1,26 @@
/**
*
* Video
*
*/
import React from 'react';
import PropTypes from 'prop-types';
/* eslint-disable jsx-a11y/media-has-caption */
const Video = props => {
const { height, src, width } = props.contentState.getEntity(props.entityKey).getData();
return (
<video height={height} width={width} style={{ maxWidth: '100%' }} controls>
<source src={src} />
</video>
);
};
Video.propTypes = {
contentState: PropTypes.object.isRequired,
entityKey: PropTypes.string.isRequired,
};
export default Video;

View File

@ -9,13 +9,31 @@ import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import styles from './styles.scss';
/* eslint-disable jsx-a11y/label-has-for */
const WysiwygBottomControls = ({ isPreviewMode, onChange, onClick }) => {
const browse = (
<FormattedMessage id="components.WysiwygBottomControls.uploadFiles.browse">
{(message) => <span className={styles.underline}>{message}</span>}
</FormattedMessage>
);
const WysiwygBottomControls = ({ charactersNumber, onClick }) => {
return (
<div className={styles.wysiwygBottomControlsWrapper}>
<div>
<span>{charactersNumber}&nbsp;</span>
<FormattedMessage id="components.WysiwygBottomControls.charactersIndicators" />
<label
className={styles.dropLabel}
onClick={(e) => {
if (isPreviewMode) {
e.preventDefault();
}
}}
>
<FormattedMessage
id="components.WysiwygBottomControls.uploadFiles"
values={{ browse }}
/>
<input type="file" onChange={onChange} />
</label>
</div>
<div className={styles.fullScreenWrapper} onClick={onClick}>
<FormattedMessage id="components.WysiwygBottomControls.fullscreen" />
@ -25,12 +43,14 @@ const WysiwygBottomControls = ({ charactersNumber, onClick }) => {
};
WysiwygBottomControls.defaultProps = {
charactersNumber: 0,
isPreviewMode: false,
onChange: () => {},
onClick: () => {},
};
WysiwygBottomControls.propTypes = {
charactersNumber: PropTypes.number,
isPreviewMode: PropTypes.bool,
onChange: PropTypes.func,
onClick: PropTypes.func,
};

View File

@ -25,3 +25,15 @@
font-size: 12px;
}
}
.underline {
color: #1C5DE7;
text-decoration: underline;
cursor: pointer;
}
.dropLabel {
> input {
display: none;
}
}

View File

@ -31,21 +31,27 @@ class Li extends React.Component {
getUnit = (value) => {
let unit;
let divider;
switch (true) {
case value > 10000:
unit = 'GB';
divider = 1000;
break;
case value < 1:
unit = 'B';
divider = 1;
break;
case value > 1000:
unit = 'MB';
divider = 1000;
break;
default:
unit = 'KB';
divider = 1;
}
return unit;
return { divider, unit };
}
handleClick = (e) => {
@ -111,10 +117,11 @@ class Li extends React.Component {
}
if (value === 'size') {
const unit = this.getUnit(item[value]);
const { divider, unit } = this.getUnit(item[value]);
const size = item[value]/divider;
return (
<div key={key} className={styles.truncate}>{item[value]}&nbsp;{unit}</div>
<div key={key} className={styles.truncate}>{Math.round(size * 100) / 100 }&nbsp;{unit}</div>
);
}