LeftPane: Measure content above list to calculate list size
This commit is contained in:
parent
5e3bbf1dc0
commit
d5925ac9d5
5 changed files with 67 additions and 74 deletions
|
@ -5206,8 +5206,8 @@ button.module-image__border-overlay:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-left-pane__header {
|
.module-left-pane__header {
|
||||||
flex-shrink: 0;
|
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-left-pane__archive-header {
|
.module-left-pane__archive-header {
|
||||||
|
@ -5295,13 +5295,20 @@ button.module-image__border-overlay:focus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-left-pane__list {
|
.module-left-pane__list--measure {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
|
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.module-left-pane__list--wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-left-pane__list {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
.module-left-pane__virtual-list {
|
.module-left-pane__virtual-list {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,12 +56,6 @@ export class LeftPane extends React.Component<PropsType> {
|
||||||
public containerRef = React.createRef<HTMLDivElement>();
|
public containerRef = React.createRef<HTMLDivElement>();
|
||||||
public setFocusToFirstNeeded = false;
|
public setFocusToFirstNeeded = false;
|
||||||
public setFocusToLastNeeded = false;
|
public setFocusToLastNeeded = false;
|
||||||
public state = {
|
|
||||||
dimensions: {
|
|
||||||
height: 0,
|
|
||||||
width: 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
public renderRow = ({
|
public renderRow = ({
|
||||||
index,
|
index,
|
||||||
|
@ -165,10 +159,6 @@ export class LeftPane extends React.Component<PropsType> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public handleResize = ({ bounds }: { bounds: BoundingRect }) => {
|
|
||||||
this.setState({ dimensions: bounds });
|
|
||||||
};
|
|
||||||
|
|
||||||
public handleFocus = () => {
|
public handleFocus = () => {
|
||||||
const { selectedConversationId } = this.props;
|
const { selectedConversationId } = this.props;
|
||||||
const { current: container } = this.containerRef;
|
const { current: container } = this.containerRef;
|
||||||
|
@ -295,7 +285,10 @@ export class LeftPane extends React.Component<PropsType> {
|
||||||
: conversations.length + (archivedConversations.length ? 1 : 0);
|
: conversations.length + (archivedConversations.length ? 1 : 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
public renderList = (): JSX.Element | Array<JSX.Element | null> => {
|
public renderList = ({
|
||||||
|
height,
|
||||||
|
width,
|
||||||
|
}: BoundingRect): JSX.Element | Array<JSX.Element | null> => {
|
||||||
const {
|
const {
|
||||||
archivedConversations,
|
archivedConversations,
|
||||||
i18n,
|
i18n,
|
||||||
|
@ -311,6 +304,8 @@ export class LeftPane extends React.Component<PropsType> {
|
||||||
return (
|
return (
|
||||||
<SearchResults
|
<SearchResults
|
||||||
{...searchResults}
|
{...searchResults}
|
||||||
|
height={height || 0}
|
||||||
|
width={width || 0}
|
||||||
openConversationInternal={openConversationInternal}
|
openConversationInternal={openConversationInternal}
|
||||||
startNewConversation={startNewConversation}
|
startNewConversation={startNewConversation}
|
||||||
renderMessageSearchResult={renderMessageSearchResult}
|
renderMessageSearchResult={renderMessageSearchResult}
|
||||||
|
@ -327,24 +322,16 @@ export class LeftPane extends React.Component<PropsType> {
|
||||||
|
|
||||||
const length = this.getLength();
|
const length = this.getLength();
|
||||||
|
|
||||||
const archived = showArchived ? (
|
|
||||||
<div className="module-left-pane__archive-helper-text" key={0}>
|
|
||||||
{i18n('archiveHelperText')}
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
// We ensure that the listKey differs between inbox and archive views, which ensures
|
// We ensure that the listKey differs between inbox and archive views, which ensures
|
||||||
// that AutoSizer properly detects the new size of its slot in the flexbox. The
|
// that AutoSizer properly detects the new size of its slot in the flexbox. The
|
||||||
// archive explainer text at the top of the archive view causes problems otherwise.
|
// archive explainer text at the top of the archive view causes problems otherwise.
|
||||||
// It also ensures that we scroll to the top when switching views.
|
// It also ensures that we scroll to the top when switching views.
|
||||||
const listKey = showArchived ? 1 : 0;
|
const listKey = showArchived ? 1 : 0;
|
||||||
|
|
||||||
const { height, width } = this.state.dimensions;
|
|
||||||
|
|
||||||
// Note: conversations is not a known prop for List, but it is required to ensure that
|
// Note: conversations is not a known prop for List, but it is required to ensure that
|
||||||
// it re-renders when our conversation data changes. Otherwise it would just render
|
// it re-renders when our conversation data changes. Otherwise it would just render
|
||||||
// on startup and scroll.
|
// on startup and scroll.
|
||||||
const list = (
|
return (
|
||||||
<div
|
<div
|
||||||
aria-live="polite"
|
aria-live="polite"
|
||||||
className="module-left-pane__list"
|
className="module-left-pane__list"
|
||||||
|
@ -358,19 +345,17 @@ export class LeftPane extends React.Component<PropsType> {
|
||||||
<List
|
<List
|
||||||
className="module-left-pane__virtual-list"
|
className="module-left-pane__virtual-list"
|
||||||
conversations={conversations}
|
conversations={conversations}
|
||||||
height={height}
|
height={height || 0}
|
||||||
onScroll={this.onScroll}
|
onScroll={this.onScroll}
|
||||||
ref={this.listRef}
|
ref={this.listRef}
|
||||||
rowCount={length}
|
rowCount={length}
|
||||||
rowHeight={68}
|
rowHeight={68}
|
||||||
rowRenderer={this.renderRow}
|
rowRenderer={this.renderRow}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
width={width}
|
width={width || 0}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return [archived, list];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public renderArchivedHeader = (): JSX.Element => {
|
public renderArchivedHeader = (): JSX.Element => {
|
||||||
|
@ -392,6 +377,7 @@ export class LeftPane extends React.Component<PropsType> {
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const {
|
const {
|
||||||
|
i18n,
|
||||||
renderExpiredBuildDialog,
|
renderExpiredBuildDialog,
|
||||||
renderMainHeader,
|
renderMainHeader,
|
||||||
renderNetworkStatus,
|
renderNetworkStatus,
|
||||||
|
@ -400,21 +386,31 @@ export class LeftPane extends React.Component<PropsType> {
|
||||||
showArchived,
|
showArchived,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
/* tslint:disable no-non-null-assertion */
|
||||||
return (
|
return (
|
||||||
<Measure bounds={true} onResize={this.handleResize}>
|
<div className="module-left-pane">
|
||||||
{({ measureRef }: MeasuredComponentProps) => (
|
<div className="module-left-pane__header">
|
||||||
<div className="module-left-pane" ref={measureRef}>
|
{showArchived ? this.renderArchivedHeader() : renderMainHeader()}
|
||||||
<div className="module-left-pane__header">
|
</div>
|
||||||
{showArchived ? this.renderArchivedHeader() : renderMainHeader()}
|
{renderExpiredBuildDialog()}
|
||||||
</div>
|
{renderNetworkStatus()}
|
||||||
{renderExpiredBuildDialog()}
|
{renderUpdateDialog()}
|
||||||
{renderNetworkStatus()}
|
{renderRelinkDialog()}
|
||||||
{renderUpdateDialog()}
|
{showArchived && (
|
||||||
{renderRelinkDialog()}
|
<div className="module-left-pane__archive-helper-text" key={0}>
|
||||||
{this.renderList()}
|
{i18n('archiveHelperText')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Measure>
|
<Measure bounds={true}>
|
||||||
|
{({ contentRect, measureRef }: MeasuredComponentProps) => (
|
||||||
|
<div className="module-left-pane__list--measure" ref={measureRef}>
|
||||||
|
<div className="module-left-pane__list--wrapper">
|
||||||
|
{this.renderList(contentRect.bounds!)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Measure>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,7 @@ messageLookup.set('4-guid-guid-guid-guid-guid', {
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
discussionsLoading: false,
|
discussionsLoading: false,
|
||||||
|
height: 700,
|
||||||
items: [],
|
items: [],
|
||||||
i18n,
|
i18n,
|
||||||
messagesLoading: false,
|
messagesLoading: false,
|
||||||
|
@ -135,6 +136,7 @@ const defaultProps = {
|
||||||
selectedConversationId: undefined,
|
selectedConversationId: undefined,
|
||||||
selectedMessageId: undefined,
|
selectedMessageId: undefined,
|
||||||
startNewConversation: action('start-new-conversation'),
|
startNewConversation: action('start-new-conversation'),
|
||||||
|
width: 320,
|
||||||
};
|
};
|
||||||
|
|
||||||
const conversations = [
|
const conversations = [
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';
|
||||||
AutoSizer,
|
|
||||||
CellMeasurer,
|
|
||||||
CellMeasurerCache,
|
|
||||||
List,
|
|
||||||
} from 'react-virtualized';
|
|
||||||
import { debounce, get, isNumber } from 'lodash';
|
import { debounce, get, isNumber } from 'lodash';
|
||||||
|
|
||||||
import { Intl } from './Intl';
|
import { Intl } from './Intl';
|
||||||
|
@ -86,6 +81,8 @@ type PropsHousekeepingType = {
|
||||||
query: string,
|
query: string,
|
||||||
options: { regionCode: string }
|
options: { regionCode: string }
|
||||||
) => void;
|
) => void;
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
|
||||||
renderMessageSearchResult: (id: string) => JSX.Element;
|
renderMessageSearchResult: (id: string) => JSX.Element;
|
||||||
};
|
};
|
||||||
|
@ -121,8 +118,6 @@ type OnScrollParamsType = {
|
||||||
export class SearchResults extends React.Component<PropsType, StateType> {
|
export class SearchResults extends React.Component<PropsType, StateType> {
|
||||||
public setFocusToFirstNeeded = false;
|
public setFocusToFirstNeeded = false;
|
||||||
public setFocusToLastNeeded = false;
|
public setFocusToLastNeeded = false;
|
||||||
public mostRecentWidth = 0;
|
|
||||||
public mostRecentHeight = 0;
|
|
||||||
public cellSizeCache = new CellMeasurerCache({
|
public cellSizeCache = new CellMeasurerCache({
|
||||||
defaultHeight: 80,
|
defaultHeight: 80,
|
||||||
fixedWidth: true,
|
fixedWidth: true,
|
||||||
|
@ -454,7 +449,7 @@ export class SearchResults extends React.Component<PropsType, StateType> {
|
||||||
parent,
|
parent,
|
||||||
style,
|
style,
|
||||||
}: RowRendererParamsType): JSX.Element => {
|
}: RowRendererParamsType): JSX.Element => {
|
||||||
const { items } = this.props;
|
const { items, width } = this.props;
|
||||||
|
|
||||||
const row = items[index];
|
const row = items[index];
|
||||||
|
|
||||||
|
@ -466,7 +461,7 @@ export class SearchResults extends React.Component<PropsType, StateType> {
|
||||||
key={key}
|
key={key}
|
||||||
parent={parent}
|
parent={parent}
|
||||||
rowIndex={index}
|
rowIndex={index}
|
||||||
width={this.mostRecentWidth}
|
width={width}
|
||||||
>
|
>
|
||||||
{this.renderRowContents(row)}
|
{this.renderRowContents(row)}
|
||||||
</CellMeasurer>
|
</CellMeasurer>
|
||||||
|
@ -530,11 +525,13 @@ export class SearchResults extends React.Component<PropsType, StateType> {
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {
|
const {
|
||||||
|
height,
|
||||||
i18n,
|
i18n,
|
||||||
items,
|
items,
|
||||||
noResults,
|
noResults,
|
||||||
searchConversationName,
|
searchConversationName,
|
||||||
searchTerm,
|
searchTerm,
|
||||||
|
width,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { scrollToIndex } = this.state;
|
const { scrollToIndex } = this.state;
|
||||||
|
|
||||||
|
@ -581,30 +578,21 @@ export class SearchResults extends React.Component<PropsType, StateType> {
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
onFocus={this.handleFocus}
|
onFocus={this.handleFocus}
|
||||||
>
|
>
|
||||||
<AutoSizer>
|
<List
|
||||||
{({ height, width }) => {
|
className="module-search-results__virtual-list"
|
||||||
this.mostRecentWidth = width;
|
deferredMeasurementCache={this.cellSizeCache}
|
||||||
this.mostRecentHeight = height;
|
height={height}
|
||||||
|
items={items}
|
||||||
return (
|
overscanRowCount={5}
|
||||||
<List
|
ref={this.listRef}
|
||||||
className="module-search-results__virtual-list"
|
rowCount={this.getRowCount()}
|
||||||
deferredMeasurementCache={this.cellSizeCache}
|
rowHeight={this.cellSizeCache.rowHeight}
|
||||||
height={height}
|
rowRenderer={this.renderRow}
|
||||||
items={items}
|
scrollToIndex={scrollToIndex}
|
||||||
overscanRowCount={5}
|
tabIndex={-1}
|
||||||
ref={this.listRef}
|
onScroll={this.onScroll as any}
|
||||||
rowCount={this.getRowCount()}
|
width={width}
|
||||||
rowHeight={this.cellSizeCache.rowHeight}
|
/>
|
||||||
rowRenderer={this.renderRow}
|
|
||||||
scrollToIndex={scrollToIndex}
|
|
||||||
tabIndex={-1}
|
|
||||||
onScroll={this.onScroll as any}
|
|
||||||
width={width}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</AutoSizer>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11522,7 +11522,7 @@
|
||||||
"rule": "React-createRef",
|
"rule": "React-createRef",
|
||||||
"path": "ts/components/SearchResults.js",
|
"path": "ts/components/SearchResults.js",
|
||||||
"line": " this.listRef = react_1.default.createRef();",
|
"line": " this.listRef = react_1.default.createRef();",
|
||||||
"lineNumber": 26,
|
"lineNumber": 24,
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2019-08-09T00:44:31.008Z",
|
"updated": "2019-08-09T00:44:31.008Z",
|
||||||
"reasonDetail": "SearchResults needs to interact with its child List directly"
|
"reasonDetail": "SearchResults needs to interact with its child List directly"
|
||||||
|
|
Loading…
Add table
Reference in a new issue