Merge pull request #140 from jorilallo/upload-fix

Editor Fixes (5 in 1)
This commit is contained in:
Jori Lallo
2017-07-15 13:48:45 -07:00
committed by GitHub
9 changed files with 174 additions and 77 deletions

View File

@@ -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"

View File

@@ -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%;
`;

View File

@@ -1,6 +1,9 @@
.editor {
color: #1b2631;
font-weight: 400;
font-size: 1em;
line-height: 1.5em;
width: 100%;
color: #1b2631;
h1,
h2,

View File

@@ -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();
}

View File

@@ -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';