// @flow import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import { observable, computed } from 'mobx'; import { observer, inject } from 'mobx-react'; import { withRouter } from 'react-router'; import { Search } from 'js-search'; import ArrowKeyNavigation from 'boundless-arrow-key-navigation'; import _ from 'lodash'; import styled from 'styled-components'; import { size } from 'styles/constants'; import Modal from 'components/Modal'; import Input from 'components/Input'; import Labeled from 'components/Labeled'; import Flex from 'components/Flex'; import PathToDocument from './components/PathToDocument'; import Document from 'models/Document'; import DocumentsStore from 'stores/DocumentsStore'; import CollectionsStore, { type DocumentPath } from 'stores/CollectionsStore'; type Props = { match: Object, history: Object, document: Document, documents: DocumentsStore, collections: CollectionsStore, }; @observer class DocumentMove extends Component { props: Props; firstDocument: HTMLElement; @observable searchTerm: ?string; @observable isSaving: boolean; @computed get searchIndex() { const { document, collections } = this.props; const paths = collections.pathsToDocuments; const index = new Search('id'); index.addIndex('title'); // Build index paths.forEach(path => { // TMP: For now, exclude paths to other collections if (_.first(path.path).id !== document.collection.id) return; index.addDocuments([path]); }); return index; } @computed get results(): Array { const { document, collections } = this.props; let results = []; if (collections.isLoaded) { if (this.searchTerm) { // Search by the keyword results = this.searchIndex.search(this.searchTerm); } else { // Default results, root of the current collection results = []; document.collection.documents.forEach(doc => { const path = collections.getPathForDocument(doc.id); if (doc && path) { results.push(path); } }); } } if (document && document.parentDocumentId) { // Add root if document does have a parent document const rootPath = collections.getPathForDocument(document.collection.id); if (rootPath) { results = [rootPath, ...results]; } } // Exclude root from search results if document is already at the root if (!document.parentDocumentId) { results = results.filter(result => result.id !== document.collection.id); } // Exclude document if on the path to result, or the same result results = results.filter(result => { return ( !result.path.map(doc => doc.id).includes(document.id) && !result.path.map(doc => doc.id).includes(document.parentDocumentId) ); }); return results; } handleKeyDown = ev => { // Down if (ev.which === 40) { ev.preventDefault(); if (this.firstDocument) { const element = ReactDOM.findDOMNode(this.firstDocument); if (element instanceof HTMLElement) element.focus(); } } }; handleClose = () => { this.props.history.push(this.props.document.url); }; handleFilter = (e: SyntheticInputEvent) => { this.searchTerm = e.target.value; }; setFirstDocumentRef = ref => { this.firstDocument = ref; }; renderPathToCurrentDocument() { const { collections, document } = this.props; const result = collections.getPathForDocument(document.id); if (result) { return ; } } render() { const { document, collections } = this.props; return ( {document && collections.isLoaded &&
{this.renderPathToCurrentDocument()}
{this.results.map((result, index) => ( index === 0 && this.setFirstDocumentRef(ref)} onSuccess={this.handleClose} /> ))}
}
); } } const Section = styled(Flex)` margin-bottom: ${size.huge}; `; const StyledArrowKeyNavigation = styled(ArrowKeyNavigation)` display: flex; flex-direction: column; flex: 1; `; export default withRouter(inject('documents', 'collections')(DocumentMove));