fix: Virtualize move document listbox (#1650)

* fix: Virtualize move document listbox
closes #1645
This commit is contained in:
Tom Moor 2020-11-12 22:02:56 -08:00 committed by GitHub
parent 32d3053002
commit 7bdcba46b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 61 deletions

View File

@ -14,6 +14,7 @@ type Props = {
document?: ?Document,
collection: ?Collection,
onSuccess?: () => void,
style?: Object,
ref?: (?React.ElementRef<"div">) => void,
};
@ -34,13 +35,20 @@ class PathToDocument extends React.Component<Props> {
};
render() {
const { result, collection, document, ref } = this.props;
const { result, collection, document, ref, style } = this.props;
const Component = document ? ResultWrapperLink : ResultWrapper;
if (!result) return <div />;
return (
<Component ref={ref} onClick={this.handleClick} href="" selectable>
<Component
ref={ref}
onClick={this.handleClick}
href=""
style={style}
role="option"
selectable
>
{collection && <CollectionIcon collection={collection} />}
&nbsp;
{result.path
@ -72,21 +80,27 @@ const StyledGoToIcon = styled(GoToIcon)`
const ResultWrapper = styled.div`
display: flex;
margin-bottom: 10px;
margin-left: -4px;
user-select: none;
color: ${(props) => props.theme.text};
cursor: default;
svg {
flex-shrink: 0;
}
`;
const ResultWrapperLink = styled(ResultWrapper.withComponent("a"))`
margin: 0 -8px;
padding: 8px 4px;
${DocumentTitle} {
display: none;
}
svg {
flex-shrink: 0;
}
&:hover,
&:active,
&:focus {

View File

@ -1,13 +1,12 @@
// @flow
import ArrowKeyNavigation from "boundless-arrow-key-navigation";
import { Search } from "js-search";
import { last } from "lodash";
import { observable, computed } from "mobx";
import { observer, inject } from "mobx-react";
import * as React from "react";
import ReactDOM from "react-dom";
import AutoSizer from "react-virtualized-auto-sizer";
import { FixedSizeList as List } from "react-window";
import styled from "styled-components";
import CollectionsStore, { type DocumentPath } from "stores/CollectionsStore";
import DocumentsStore from "stores/DocumentsStore";
import UiStore from "stores/UiStore";
@ -28,7 +27,6 @@ type Props = {|
@observer
class DocumentMove extends React.Component<Props> {
firstDocument: ?PathToDocument;
@observable searchTerm: ?string;
@observable isSaving: boolean;
@ -87,17 +85,6 @@ class DocumentMove extends React.Component<Props> {
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();
}
}
};
handleSuccess = () => {
this.props.ui.showToast("Document moved");
this.props.onRequestClose();
@ -107,10 +94,6 @@ class DocumentMove extends React.Component<Props> {
this.searchTerm = ev.target.value;
};
setFirstDocumentRef = (ref) => {
this.firstDocument = ref;
};
renderPathToCurrentDocument() {
const { collections, document } = this.props;
const result = collections.getPathForDocument(document.id);
@ -125,8 +108,24 @@ class DocumentMove extends React.Component<Props> {
}
}
row = ({ index, data, style }) => {
const result = data[index];
const { document, collections } = this.props;
return (
<PathToDocument
result={result}
document={document}
collection={collections.get(result.collectionId)}
onSuccess={this.handleSuccess}
style={style}
/>
);
};
render() {
const { document, collections, onRequestClose } = this.props;
const data = this.results;
return (
<Modal isOpen onRequestClose={onRequestClose} title="Move document">
@ -145,33 +144,29 @@ class DocumentMove extends React.Component<Props> {
<Input
type="search"
placeholder="Search collections & documents…"
onKeyDown={this.handleKeyDown}
onChange={this.handleFilter}
required
autoFocus
/>
</InputWrapper>
<Results>
<Flex column>
<StyledArrowKeyNavigation
mode={ArrowKeyNavigation.mode.VERTICAL}
defaultActiveChildIndex={0}
>
{this.results.map((result, index) => (
<PathToDocument
key={result.id}
result={result}
document={document}
collection={collections.get(result.collectionId)}
ref={(ref) =>
index === 0 && this.setFirstDocumentRef(ref)
}
onSuccess={this.handleSuccess}
/>
))}
</StyledArrowKeyNavigation>
</Flex>
<AutoSizer>
{({ width, height }) => (
<Flex role="listbox" column>
<List
key={data.length}
width={width}
height={height}
itemData={data}
itemCount={data.length}
itemSize={40}
itemKey={(index, data) => data[index].id}
>
{this.row}
</List>
</Flex>
)}
</AutoSizer>
</Results>
</NewLocation>
</Section>
@ -202,25 +197,18 @@ const Input = styled("input")`
`;
const NewLocation = styled(Outline)`
flex-direction: column;
display: block;
flex: initial;
height: 40vh;
`;
const Results = styled(Flex)`
display: block;
width: 100%;
max-height: 40vh;
overflow-y: auto;
padding: 8px;
const Results = styled.div`
padding: 8px 0;
height: calc(100% - 46px);
`;
const Section = styled(Flex)`
margin-bottom: 24px;
`;
const StyledArrowKeyNavigation = styled(ArrowKeyNavigation)`
display: flex;
flex-direction: column;
flex: 1;
`;
export default inject("documents", "collections", "ui")(DocumentMove);

View File

@ -141,7 +141,9 @@
"react-modal": "^3.1.2",
"react-portal": "^4.0.0",
"react-router-dom": "^5.1.2",
"react-virtualized-auto-sizer": "^1.0.2",
"react-waypoint": "^9.0.2",
"react-window": "^1.8.6",
"rich-markdown-editor": "^11.0.6",
"semver": "^7.3.2",
"sequelize": "^6.3.4",

View File

@ -962,10 +962,10 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.4.0", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.11.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.4.0", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.12.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
dependencies:
regenerator-runtime "^0.13.4"
@ -7719,6 +7719,11 @@ mem@^4.0.0:
mimic-fn "^2.0.0"
p-is-promise "^2.0.0"
"memoize-one@>=3.1.1 <6":
version "5.1.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
memoizee@^0.4.14:
version "0.4.14"
resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57"
@ -9470,6 +9475,11 @@ react-side-effect@^1.1.0:
dependencies:
shallowequal "^1.0.1"
react-virtualized-auto-sizer@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.2.tgz#a61dd4f756458bbf63bd895a92379f9b70f803bd"
integrity sha512-MYXhTY1BZpdJFjUovvYHVBmkq79szK/k7V3MO+36gJkWGkrXKtyr4vCPtpphaTLRAdDNoYEYFZWE8LjN+PIHNg==
react-waypoint@^9.0.2:
version "9.0.2"
resolved "https://registry.yarnpkg.com/react-waypoint/-/react-waypoint-9.0.2.tgz#d65fb0fe6ff5c1b832a1d01b1462a661fb921e45"
@ -9479,6 +9489,14 @@ react-waypoint@^9.0.2:
prop-types "^15.0.0"
react-is "^16.6.3"
react-window@^1.8.6:
version "1.8.6"
resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.6.tgz#d011950ac643a994118632665aad0c6382e2a112"
integrity sha512-8VwEEYyjz6DCnGBsd+MgkD0KJ2/OXFULyDtorIiTz+QzwoP94tBoA7CnbtyXMm+cCeAUER5KJcPtWl9cpKbOBg==
dependencies:
"@babel/runtime" "^7.0.0"
memoize-one ">=3.1.1 <6"
react@^16.8.6:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"