* WIP: Slate editor
* WIP
* Focus at start / end working
* ah ha
* Super basic floating toolbar
* Nested list editing
* Pulling more logic into plugins
* inline code markdown
* Backspace at end of code block should remove mark
* Ensure there is always an empty line at editor end
* Add keyboard shortcuts for bold, italic, underline
* Add strikethrough shortcode and toolbar
* Toolbar to declarative
Fixed paragraph styling
Removed unused stuffs
* Super basic link editing
* Split Toolbar, now possible to edit and remove links
* Add new link to selection from toolbar working
* Ensure toolbar doesn't extend off screen
* Fix minor js issues, disable formatting of document title
* Boom, icons
* Remove codemirror, fix MD parsing issues
* CMD+S now saves inplace
* Add --- shortcut for horizontal rule
* Improved styling for link editor
* Add header anchors in readOnly
* More readable core text color
* Restored image file uploading 🎉
* Add support for inline md syntax, ** __ etc
* Centered
* Flooooow
* Checklist support
* Upgrade edit list plugin
* Finally. Allow keydown within rich textarea
* Update Markdown serializer
* Cleanup, remove async editor loading
* Editor > MarkdownEditor
Fixed unsaved changes warning triggered when all changes are saved
* MOAR typing
* Combine edit and view
* Fixed checkboxes still editable in readOnly
* wip
* Breadcrumb
Restored scroll
* Move document scene actions to menu
* Added: Support for code blocks, syntax highlighting
* Cleanup
* > styled component
* Prevent CMD+Enter from adding linebreak
* Show image uploading in layout activity indicator
* Upgrade editor deps
* Improve link toolbar. Only one scenario where it's not working now
151 lines
4.0 KiB
JavaScript
151 lines
4.0 KiB
JavaScript
// @flow
|
|
import React, { Component } from 'react';
|
|
import get from 'lodash/get';
|
|
import { observer } from 'mobx-react';
|
|
import { browserHistory, withRouter } from 'react-router';
|
|
import { Flex } from 'reflexbox';
|
|
|
|
import DocumentStore from './DocumentStore';
|
|
import Breadcrumbs from './components/Breadcrumbs';
|
|
import Editor from './components/Editor';
|
|
import Menu from './components/Menu';
|
|
import Layout, { HeaderAction, SaveAction } from 'components/Layout';
|
|
import AtlasPreviewLoading from 'components/AtlasPreviewLoading';
|
|
import CenteredContent from 'components/CenteredContent';
|
|
|
|
const DISCARD_CHANGES = `
|
|
You have unsaved changes.
|
|
Are you sure you want to discard them?
|
|
`;
|
|
|
|
type Props = {
|
|
route: Object,
|
|
router: Object,
|
|
params: Object,
|
|
keydown: Object,
|
|
};
|
|
|
|
@withRouter
|
|
@observer
|
|
class Document extends Component {
|
|
store: DocumentStore;
|
|
props: Props;
|
|
|
|
constructor(props: Props) {
|
|
super(props);
|
|
this.store = new DocumentStore({});
|
|
}
|
|
|
|
componentDidMount = () => {
|
|
if (this.props.route.newDocument) {
|
|
this.store.collectionId = this.props.params.id;
|
|
this.store.newDocument = true;
|
|
} else if (this.props.route.editDocument) {
|
|
this.store.documentId = this.props.params.id;
|
|
this.store.fetchDocument();
|
|
} else if (this.props.route.newChildDocument) {
|
|
this.store.documentId = this.props.params.id;
|
|
this.store.newChildDocument = true;
|
|
this.store.fetchDocument();
|
|
} else {
|
|
this.store.documentId = this.props.params.id;
|
|
this.store.newDocument = false;
|
|
this.store.fetchDocument();
|
|
}
|
|
|
|
// Prevent user from accidentally leaving with unsaved changes
|
|
const remove = this.props.router.setRouteLeaveHook(this.props.route, () => {
|
|
if (this.store.hasPendingChanges) {
|
|
return confirm(DISCARD_CHANGES);
|
|
}
|
|
remove();
|
|
return null;
|
|
});
|
|
};
|
|
|
|
onEdit = () => {
|
|
const url = `${this.store.document.url}/edit`;
|
|
browserHistory.push(url);
|
|
};
|
|
|
|
onSave = (options: { redirect?: boolean } = {}) => {
|
|
if (this.store.newDocument || this.store.newChildDocument) {
|
|
this.store.saveDocument(options);
|
|
} else {
|
|
this.store.updateDocument(options);
|
|
}
|
|
};
|
|
|
|
onImageUploadStart = () => {
|
|
this.store.updateUploading(true);
|
|
};
|
|
|
|
onImageUploadStop = () => {
|
|
this.store.updateUploading(false);
|
|
};
|
|
|
|
onCancel = () => {
|
|
browserHistory.goBack();
|
|
};
|
|
|
|
render() {
|
|
const { route } = this.props;
|
|
const isNew = route.newDocument || route.newChildDocument;
|
|
const isEditing = route.editDocument;
|
|
const title = (
|
|
<Breadcrumbs
|
|
document={this.store.document}
|
|
pathToDocument={this.store.pathToDocument}
|
|
/>
|
|
);
|
|
const titleText = `${get(this.store, 'document.collection.name')} - ${get(this.store, 'document.title')}`;
|
|
|
|
const actions = (
|
|
<Flex>
|
|
<HeaderAction>
|
|
{isEditing
|
|
? <SaveAction
|
|
onClick={this.onSave}
|
|
disabled={this.store.isSaving}
|
|
isNew={isNew}
|
|
/>
|
|
: <a onClick={this.onEdit}>Edit</a>}
|
|
</HeaderAction>
|
|
<Menu
|
|
store={this.store}
|
|
document={this.store.document}
|
|
collectionTree={this.store.collectionTree}
|
|
/>
|
|
</Flex>
|
|
);
|
|
|
|
return (
|
|
<Layout
|
|
actions={actions}
|
|
title={title}
|
|
titleText={titleText}
|
|
loading={this.store.isSaving || this.store.isUploading}
|
|
search={false}
|
|
fixed
|
|
>
|
|
{this.store.isFetching &&
|
|
<CenteredContent>
|
|
<AtlasPreviewLoading />
|
|
</CenteredContent>}
|
|
{this.store.document &&
|
|
<Editor
|
|
text={this.store.document.text}
|
|
onImageUploadStart={this.onImageUploadStart}
|
|
onImageUploadStop={this.onImageUploadStop}
|
|
onChange={this.store.updateText}
|
|
onSave={this.onSave}
|
|
onCancel={this.onCancel}
|
|
readOnly={!this.props.route.editDocument}
|
|
/>}
|
|
</Layout>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default Document;
|