mirror of
https://github.com/strapi/strapi.git
synced 2025-08-13 11:17:42 +00:00
Merge pull request #879 from strapi/improve-wys-upload
Improve file upload in WYSIWYG
This commit is contained in:
commit
5796d96ecc
@ -110,6 +110,8 @@
|
|||||||
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
|
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
|
||||||
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
|
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
|
||||||
"components.WysiwygBottomControls.charactersIndicators": "characters",
|
"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",
|
"components.WysiwygBottomControls.fullscreen": "Expand",
|
||||||
|
|
||||||
"notification.error": "Ein Fehler ist aufgetreten",
|
"notification.error": "Ein Fehler ist aufgetreten",
|
||||||
|
@ -116,6 +116,8 @@
|
|||||||
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
|
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
|
||||||
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
|
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
|
||||||
"components.WysiwygBottomControls.charactersIndicators": "characters",
|
"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",
|
"components.WysiwygBottomControls.fullscreen": "Expand",
|
||||||
|
|
||||||
"notification.error": "An error occurred",
|
"notification.error": "An error occurred",
|
||||||
|
@ -116,6 +116,8 @@
|
|||||||
"components.Wysiwyg.ToggleMode.markdown": "Retour au markdown",
|
"components.Wysiwyg.ToggleMode.markdown": "Retour au markdown",
|
||||||
"components.Wysiwyg.ToggleMode.preview": "Voir la preview",
|
"components.Wysiwyg.ToggleMode.preview": "Voir la preview",
|
||||||
"components.WysiwygBottomControls.charactersIndicators": "charactères",
|
"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",
|
"components.WysiwygBottomControls.fullscreen": "Plein écran",
|
||||||
|
|
||||||
"notification.error": "Une erreur est survenue",
|
"notification.error": "Une erreur est survenue",
|
||||||
|
@ -113,6 +113,8 @@
|
|||||||
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
|
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
|
||||||
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
|
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
|
||||||
"components.WysiwygBottomControls.charactersIndicators": "characters",
|
"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",
|
"components.WysiwygBottomControls.fullscreen": "Expand",
|
||||||
|
|
||||||
"notification.error": "Wystąpił błąd",
|
"notification.error": "Wystąpił błąd",
|
||||||
|
@ -114,6 +114,8 @@
|
|||||||
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
|
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
|
||||||
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
|
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
|
||||||
"components.WysiwygBottomControls.charactersIndicators": "characters",
|
"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",
|
"components.WysiwygBottomControls.fullscreen": "Expand",
|
||||||
|
|
||||||
"notification.error": "Bir hata oluştu",
|
"notification.error": "Bir hata oluştu",
|
||||||
|
@ -118,6 +118,8 @@
|
|||||||
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
|
"components.Wysiwyg.ToggleMode.markdown": "Switch to markdown",
|
||||||
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
|
"components.Wysiwyg.ToggleMode.preview": "Switch to preview",
|
||||||
"components.WysiwygBottomControls.charactersIndicators": "characters",
|
"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",
|
"components.WysiwygBottomControls.fullscreen": "Expand",
|
||||||
|
|
||||||
"notification.error": "有錯誤發生了",
|
"notification.error": "有錯誤發生了",
|
||||||
|
@ -56,9 +56,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: we might need this later
|
// NOTE: we might need this later
|
||||||
// span {
|
span {
|
||||||
// white-space: pre-line;
|
white-space: pre-line;
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h1+.editorParagraph{
|
h1+.editorParagraph{
|
||||||
|
@ -369,28 +369,8 @@ class Wysiwyg extends React.Component {
|
|||||||
return this.setState({ isDraging: false });
|
return this.setState({ isDraging: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { dataTransfer: { files } } = e;
|
const files = e.dataTransfer ? e.dataTransfer.files : e.target.files;
|
||||||
const formData = new FormData();
|
return this.uploadFile(files);
|
||||||
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(``),
|
|
||||||
);
|
|
||||||
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 });
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleKeyCommand = (command, editorState) => {
|
handleKeyCommand = (command, editorState) => {
|
||||||
@ -410,6 +390,8 @@ class Wysiwyg extends React.Component {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handlePastedFiles = files => this.uploadFile(files);
|
||||||
|
|
||||||
handleReturn = (e, editorState) => {
|
handleReturn = (e, editorState) => {
|
||||||
const selection = editorState.getSelection();
|
const selection = editorState.getSelection();
|
||||||
const currentBlock = editorState.getCurrentContent().getBlockForKey(selection.getStartKey());
|
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(``, '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) {
|
componentDidCatch(error, info) {
|
||||||
console.log('err', error);
|
console.log('err', error);
|
||||||
console.log('info', info);
|
console.log('info', info);
|
||||||
@ -549,6 +562,7 @@ class Wysiwyg extends React.Component {
|
|||||||
blockStyleFn={getBlockStyle}
|
blockStyleFn={getBlockStyle}
|
||||||
editorState={editorState}
|
editorState={editorState}
|
||||||
handleKeyCommand={this.handleKeyCommand}
|
handleKeyCommand={this.handleKeyCommand}
|
||||||
|
handlePastedFiles={this.handlePastedFiles}
|
||||||
handleReturn={this.handleReturn}
|
handleReturn={this.handleReturn}
|
||||||
keyBindingFn={this.mapKeyToEditorCommand}
|
keyBindingFn={this.mapKeyToEditorCommand}
|
||||||
onBlur={this.handleBlur}
|
onBlur={this.handleBlur}
|
||||||
@ -563,8 +577,9 @@ class Wysiwyg extends React.Component {
|
|||||||
)}
|
)}
|
||||||
{!isFullscreen && (
|
{!isFullscreen && (
|
||||||
<WysiwygBottomControls
|
<WysiwygBottomControls
|
||||||
charactersNumber={this.getCharactersNumber()}
|
isPreviewMode={isPreviewMode}
|
||||||
onClick={this.toggleFullScreen}
|
onClick={this.toggleFullScreen}
|
||||||
|
onChange={this.handleDrop}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,10 +12,12 @@ import { isEmpty } from 'lodash';
|
|||||||
|
|
||||||
import WysiwygEditor from 'components/WysiwygEditor';
|
import WysiwygEditor from 'components/WysiwygEditor';
|
||||||
import converter from './converter';
|
import converter from './converter';
|
||||||
import { findLinkEntities, findImageEntities } from './strategies';
|
import { findLinkEntities, findImageEntities, findVideoEntities } from './strategies';
|
||||||
|
|
||||||
import Image from './image';
|
import Image from './image';
|
||||||
import Link from './link';
|
import Link from './link';
|
||||||
|
import Video from './video';
|
||||||
|
|
||||||
import styles from './componentsStyles.scss';
|
import styles from './componentsStyles.scss';
|
||||||
|
|
||||||
function getBlockStyle(block) {
|
function getBlockStyle(block) {
|
||||||
@ -61,6 +63,10 @@ class PreviewWysiwyg extends React.Component {
|
|||||||
strategy: findImageEntities,
|
strategy: findImageEntities,
|
||||||
component: Image,
|
component: Image,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
strategy: findVideoEntities,
|
||||||
|
component: Video,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const blocksFromHTML = convertFromHTML(html);
|
const blocksFromHTML = convertFromHTML(html);
|
||||||
|
@ -8,8 +8,19 @@ function findLinkEntities(contentBlock, callback, contentState) {
|
|||||||
function findImageEntities(contentBlock, callback, contentState) {
|
function findImageEntities(contentBlock, callback, contentState) {
|
||||||
contentBlock.findEntityRanges(character => {
|
contentBlock.findEntityRanges(character => {
|
||||||
const entityKey = character.getEntity();
|
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);
|
}, 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 };
|
||||||
|
@ -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;
|
@ -9,13 +9,31 @@ import PropTypes from 'prop-types';
|
|||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import styles from './styles.scss';
|
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 (
|
return (
|
||||||
<div className={styles.wysiwygBottomControlsWrapper}>
|
<div className={styles.wysiwygBottomControlsWrapper}>
|
||||||
<div>
|
<div>
|
||||||
<span>{charactersNumber} </span>
|
<label
|
||||||
<FormattedMessage id="components.WysiwygBottomControls.charactersIndicators" />
|
className={styles.dropLabel}
|
||||||
|
onClick={(e) => {
|
||||||
|
if (isPreviewMode) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id="components.WysiwygBottomControls.uploadFiles"
|
||||||
|
values={{ browse }}
|
||||||
|
/>
|
||||||
|
<input type="file" onChange={onChange} />
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.fullScreenWrapper} onClick={onClick}>
|
<div className={styles.fullScreenWrapper} onClick={onClick}>
|
||||||
<FormattedMessage id="components.WysiwygBottomControls.fullscreen" />
|
<FormattedMessage id="components.WysiwygBottomControls.fullscreen" />
|
||||||
@ -25,12 +43,14 @@ const WysiwygBottomControls = ({ charactersNumber, onClick }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
WysiwygBottomControls.defaultProps = {
|
WysiwygBottomControls.defaultProps = {
|
||||||
charactersNumber: 0,
|
isPreviewMode: false,
|
||||||
|
onChange: () => {},
|
||||||
onClick: () => {},
|
onClick: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
WysiwygBottomControls.propTypes = {
|
WysiwygBottomControls.propTypes = {
|
||||||
charactersNumber: PropTypes.number,
|
isPreviewMode: PropTypes.bool,
|
||||||
|
onChange: PropTypes.func,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,3 +25,15 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.underline {
|
||||||
|
color: #1C5DE7;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropLabel {
|
||||||
|
> input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -31,21 +31,27 @@ class Li extends React.Component {
|
|||||||
|
|
||||||
getUnit = (value) => {
|
getUnit = (value) => {
|
||||||
let unit;
|
let unit;
|
||||||
|
let divider;
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case value > 10000:
|
case value > 10000:
|
||||||
unit = 'GB';
|
unit = 'GB';
|
||||||
|
divider = 1000;
|
||||||
break;
|
break;
|
||||||
case value < 1:
|
case value < 1:
|
||||||
unit = 'B';
|
unit = 'B';
|
||||||
|
divider = 1;
|
||||||
break;
|
break;
|
||||||
case value > 1000:
|
case value > 1000:
|
||||||
unit = 'MB';
|
unit = 'MB';
|
||||||
|
divider = 1000;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
unit = 'KB';
|
unit = 'KB';
|
||||||
|
divider = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return unit;
|
return { divider, unit };
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick = (e) => {
|
handleClick = (e) => {
|
||||||
@ -111,10 +117,11 @@ class Li extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value === 'size') {
|
if (value === 'size') {
|
||||||
const unit = this.getUnit(item[value]);
|
const { divider, unit } = this.getUnit(item[value]);
|
||||||
|
const size = item[value]/divider;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={key} className={styles.truncate}>{item[value]} {unit}</div>
|
<div key={key} className={styles.truncate}>{Math.round(size * 100) / 100 } {unit}</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user