fix: Virtualize move document listbox (#1650)
* fix: Virtualize move document listbox closes #1645
This commit is contained in:
parent
32d3053002
commit
7bdcba46b8
|
@ -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} />}
|
||||
|
||||
{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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
26
yarn.lock
26
yarn.lock
|
@ -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"
|
||||
|
|
Reference in New Issue