From 7bdcba46b812c1ba0aa55e63ab26960e3f12c904 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Thu, 12 Nov 2020 22:02:56 -0800 Subject: [PATCH] fix: Virtualize move document listbox (#1650) * fix: Virtualize move document listbox closes #1645 --- app/components/PathToDocument.js | 22 ++++- .../Document/components/DocumentMove.js | 94 ++++++++----------- package.json | 2 + yarn.lock | 26 ++++- 4 files changed, 83 insertions(+), 61 deletions(-) diff --git a/app/components/PathToDocument.js b/app/components/PathToDocument.js index 147e724e..f9995e79 100644 --- a/app/components/PathToDocument.js +++ b/app/components/PathToDocument.js @@ -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 { }; render() { - const { result, collection, document, ref } = this.props; + const { result, collection, document, ref, style } = this.props; const Component = document ? ResultWrapperLink : ResultWrapper; if (!result) return
; return ( - + {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 { diff --git a/app/scenes/Document/components/DocumentMove.js b/app/scenes/Document/components/DocumentMove.js index aab06eb5..dea5f081 100644 --- a/app/scenes/Document/components/DocumentMove.js +++ b/app/scenes/Document/components/DocumentMove.js @@ -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 { - firstDocument: ?PathToDocument; @observable searchTerm: ?string; @observable isSaving: boolean; @@ -87,17 +85,6 @@ class DocumentMove extends React.Component { 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 { 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 { } } + row = ({ index, data, style }) => { + const result = data[index]; + const { document, collections } = this.props; + + return ( + + ); + }; + render() { const { document, collections, onRequestClose } = this.props; + const data = this.results; return ( @@ -145,33 +144,29 @@ class DocumentMove extends React.Component { - - - - {this.results.map((result, index) => ( - - index === 0 && this.setFirstDocumentRef(ref) - } - onSuccess={this.handleSuccess} - /> - ))} - - + + {({ width, height }) => ( + + data[index].id} + > + {this.row} + + + )} + @@ -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); diff --git a/package.json b/package.json index e7929bdf..98bfeb8a 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/yarn.lock b/yarn.lock index 40400d73..1e4ae927 100644 --- a/yarn.lock +++ b/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"