Merge pull request #140 from jorilallo/upload-fix
Editor Fixes (5 in 1)
This commit is contained in:
@@ -19,6 +19,7 @@ class DropToImport extends Component {
|
||||
activeClassName?: string,
|
||||
rejectClassName?: string,
|
||||
documents: DocumentsStore,
|
||||
disabled: boolean,
|
||||
history: Object,
|
||||
};
|
||||
state = {
|
||||
@@ -83,9 +84,12 @@ class DropToImport extends Component {
|
||||
'history',
|
||||
'documentId',
|
||||
'collectionId',
|
||||
'documents'
|
||||
'documents',
|
||||
'disabled'
|
||||
);
|
||||
|
||||
if (this.props.disabled) return this.props.children;
|
||||
|
||||
return (
|
||||
<Dropzone
|
||||
accept="text/markdown, text/plain"
|
||||
|
||||
@@ -3,8 +3,11 @@ import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Editor, Plain } from 'slate';
|
||||
import keydown from 'react-keydown';
|
||||
import classnames from 'classnames/bind';
|
||||
import type { Document, State, Editor as EditorType } from './types';
|
||||
import getDataTransferFiles from 'utils/getDataTransferFiles';
|
||||
import uploadFile from 'utils/uploadFile';
|
||||
import Flex from 'components/Flex';
|
||||
import ClickablePadding from './components/ClickablePadding';
|
||||
import Toolbar from './components/Toolbar';
|
||||
@@ -82,22 +85,73 @@ type KeyData = {
|
||||
this.props.onChange(Markdown.serialize(state));
|
||||
};
|
||||
|
||||
handleDrop = async (ev: SyntheticEvent) => {
|
||||
// check if this event was already handled by the Editor
|
||||
if (ev.isDefaultPrevented()) return;
|
||||
|
||||
// otherwise we'll handle this
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
const files = getDataTransferFiles(ev);
|
||||
for (const file of files) {
|
||||
await this.insertFile(file);
|
||||
}
|
||||
};
|
||||
|
||||
insertFile = async (file: Object) => {
|
||||
this.props.onImageUploadStart();
|
||||
const asset = await uploadFile(file);
|
||||
const state = this.editor.getState();
|
||||
const transform = state.transform();
|
||||
transform.collapseToEndOf(state.document);
|
||||
transform.insertBlock({
|
||||
type: 'image',
|
||||
isVoid: true,
|
||||
data: { src: asset.url, alt: file.name },
|
||||
});
|
||||
this.props.onImageUploadStop();
|
||||
this.setState({ state: transform.apply() });
|
||||
};
|
||||
|
||||
cancelEvent = (ev: SyntheticEvent) => {
|
||||
ev.preventDefault();
|
||||
};
|
||||
|
||||
// Handling of keyboard shortcuts outside of editor focus
|
||||
@keydown('meta+s')
|
||||
onSave(ev: SyntheticKeyboardEvent) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.props.onSave();
|
||||
}
|
||||
|
||||
@keydown('meta+enter')
|
||||
onSaveAndExit(ev: SyntheticKeyboardEvent) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.props.onSave({ redirect: false });
|
||||
}
|
||||
|
||||
@keydown('esc')
|
||||
onCancel() {
|
||||
this.props.onCancel();
|
||||
}
|
||||
|
||||
// Handling of keyboard shortcuts within editor focus
|
||||
onKeyDown = (ev: SyntheticKeyboardEvent, data: KeyData, state: State) => {
|
||||
if (!data.isMeta) return;
|
||||
|
||||
switch (data.key) {
|
||||
case 's':
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.props.onSave();
|
||||
return state;
|
||||
this.onSave(ev);
|
||||
break;
|
||||
case 'enter':
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.props.onSave({ redirect: false });
|
||||
return state;
|
||||
this.onSaveAndExit(ev);
|
||||
break;
|
||||
case 'escape':
|
||||
return this.props.onCancel();
|
||||
this.onCancel();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
};
|
||||
@@ -120,31 +174,40 @@ type KeyData = {
|
||||
|
||||
render = () => {
|
||||
return (
|
||||
<Container auto column>
|
||||
<HeaderContainer
|
||||
onClick={this.focusAtStart}
|
||||
readOnly={this.props.readOnly}
|
||||
>
|
||||
{this.props.heading}
|
||||
</HeaderContainer>
|
||||
<Toolbar state={this.state.state} onChange={this.onChange} />
|
||||
<Editor
|
||||
key={this.props.starred}
|
||||
ref={ref => (this.editor = ref)}
|
||||
placeholder="Start with a title..."
|
||||
className={cx(styles.editor, { readOnly: this.props.readOnly })}
|
||||
schema={this.schema}
|
||||
plugins={this.plugins}
|
||||
state={this.state.state}
|
||||
onChange={this.onChange}
|
||||
onDocumentChange={this.onDocumentChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onSave={this.props.onSave}
|
||||
readOnly={this.props.readOnly}
|
||||
/>
|
||||
{!this.props.readOnly &&
|
||||
<ClickablePadding onClick={this.focusAtEnd} grow />}
|
||||
</Container>
|
||||
<Flex
|
||||
onDrop={this.handleDrop}
|
||||
onDragOver={this.cancelEvent}
|
||||
onDragEnter={this.cancelEvent}
|
||||
align="flex-start"
|
||||
justify="center"
|
||||
auto
|
||||
>
|
||||
<MaxWidth column auto>
|
||||
<HeaderContainer
|
||||
onClick={this.focusAtStart}
|
||||
readOnly={this.props.readOnly}
|
||||
>
|
||||
{this.props.heading}
|
||||
</HeaderContainer>
|
||||
<Toolbar state={this.state.state} onChange={this.onChange} />
|
||||
<Editor
|
||||
key={this.props.starred}
|
||||
ref={ref => (this.editor = ref)}
|
||||
placeholder="Start with a title..."
|
||||
className={cx(styles.editor, { readOnly: this.props.readOnly })}
|
||||
schema={this.schema}
|
||||
plugins={this.plugins}
|
||||
state={this.state.state}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onChange={this.onChange}
|
||||
onDocumentChange={this.onDocumentChange}
|
||||
onSave={this.props.onSave}
|
||||
readOnly={this.props.readOnly}
|
||||
/>
|
||||
{!this.props.readOnly &&
|
||||
<ClickablePadding onClick={this.focusAtEnd} grow />}
|
||||
</MaxWidth>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -153,7 +216,8 @@ MarkdownEditor.childContextTypes = {
|
||||
starred: PropTypes.bool,
|
||||
};
|
||||
|
||||
const Container = styled(Flex)`
|
||||
const MaxWidth = styled(Flex)`
|
||||
max-width: 50em;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
.editor {
|
||||
color: #1b2631;
|
||||
font-weight: 400;
|
||||
font-size: 1em;
|
||||
line-height: 1.5em;
|
||||
width: 100%;
|
||||
color: #1b2631;
|
||||
|
||||
h1,
|
||||
h2,
|
||||
|
||||
@@ -26,8 +26,8 @@ const createPlugins = ({ onImageUploadStart, onImageUploadStop }: Options) => {
|
||||
DropOrPasteImages({
|
||||
extensions: ['png', 'jpg', 'gif'],
|
||||
applyTransform: async (transform, file) => {
|
||||
onImageUploadStart();
|
||||
try {
|
||||
onImageUploadStart();
|
||||
const asset = await uploadFile(file);
|
||||
const alt = file.name;
|
||||
const src = asset.url;
|
||||
@@ -39,6 +39,7 @@ const createPlugins = ({ onImageUploadStart, onImageUploadStop }: Options) => {
|
||||
});
|
||||
} catch (err) {
|
||||
// TODO: Show a failure alert
|
||||
console.error(err);
|
||||
} finally {
|
||||
onImageUploadStop();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import _ from 'lodash';
|
||||
import keydown from 'react-keydown';
|
||||
import Flex from 'components/Flex';
|
||||
import { color, layout } from 'styles/constants';
|
||||
import { documentEditUrl, homeUrl, searchUrl } from 'utils/routeHelpers';
|
||||
|
||||
import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
||||
import { LoadingIndicatorBar } from 'components/LoadingIndicator';
|
||||
@@ -51,15 +52,21 @@ type Props = {
|
||||
@observable modal = null;
|
||||
|
||||
@keydown(['/', 't'])
|
||||
search() {
|
||||
if (this.props.auth.authenticated)
|
||||
_.defer(() => this.props.history.push('/search'));
|
||||
goToSearch(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.props.history.push(searchUrl());
|
||||
}
|
||||
|
||||
@keydown('d')
|
||||
dashboard() {
|
||||
if (this.props.auth.authenticated)
|
||||
_.defer(() => this.props.history.push('/'));
|
||||
goToDashboard() {
|
||||
this.props.history.push(homeUrl());
|
||||
}
|
||||
|
||||
@keydown('e')
|
||||
goToEdit() {
|
||||
if (!this.props.ui.activeDocument) return;
|
||||
this.props.history.push(documentEditUrl(this.props.ui.activeDocument));
|
||||
}
|
||||
|
||||
handleLogout = () => {
|
||||
@@ -67,9 +74,9 @@ type Props = {
|
||||
};
|
||||
|
||||
@keydown('shift+/')
|
||||
handleOpenKeyboardShortcuts = () => {
|
||||
handleOpenKeyboardShortcuts() {
|
||||
this.modal = 'keyboard-shortcuts';
|
||||
};
|
||||
}
|
||||
|
||||
handleCreateCollection = () => {
|
||||
this.modal = 'create-collection';
|
||||
|
||||
Reference in New Issue
Block a user