web: People: Optimization of People list rendering

Added new packages: react-window and react-virtualized-auto-sizer;
Added changes to Scrollbar, PageLayout and PageSection
This commit is contained in:
Alexey Safronov 2019-08-07 13:57:54 +03:00
parent f308c669e1
commit b0d5d1ea72
7 changed files with 183 additions and 91 deletions

View File

@ -26,6 +26,8 @@
"react-router": "5.0.1",
"react-router-dom": "5.0.1",
"react-scripts": "3.0.1",
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.5",
"reactstrap": "8.0.0",
"redux": "4.0.1",
"redux-form": "^8.2.4",

View File

@ -1,76 +1,151 @@
import React from "react";
import React, { memo, useCallback } from "react";
import { withRouter } from "react-router";
import { connect } from "react-redux";
import { ContentRow, toastr } from "asc-web-components";
import { ContentRow, toastr, Scrollbar } from "asc-web-components";
import UserContent from "./userContent";
//import config from "../../../../../../package.json";
import { selectUser, deselectUser, setSelection } from "../../../../../store/people/actions";
import { isUserSelected, getUserStatus, getUserRole, isUserDisabled } from '../../../../../store/people/selectors';
import { isAdmin } from '../../../../../store/auth/selectors';
import {
selectUser,
deselectUser,
setSelection
} from "../../../../../store/people/actions";
import {
isUserSelected,
getUserStatus,
getUserRole,
isUserDisabled
} from "../../../../../store/people/selectors";
import { isAdmin } from "../../../../../store/auth/selectors";
import { FixedSizeList as List, areEqual } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
const CustomScrollbars = ({ onScroll, forwardedRef, style, children }) => {
const refSetter = useCallback(scrollbarsRef => {
if (scrollbarsRef) {
forwardedRef(scrollbarsRef.view);
} else {
forwardedRef(null);
}
}, [forwardedRef]);
return (
<Scrollbar
ref={refSetter}
style={{ ...style, overflow: "hidden" }}
onScroll={onScroll}
stype="mediumBlack"
>
{children}
</Scrollbar>
);
};
const CustomScrollbarsVirtualList = React.forwardRef((props, ref) => (
<CustomScrollbars {...props} forwardedRef={ref} />
));
const Row = memo(
({
data,
index,
style,
onContentRowSelect,
history,
settings,
selection,
getUserContextOptions
}) => {
// Data passed to List as "itemData" is available as props.data
const user = data[index];
// console.log("Row user", user);
const contextOptions = getUserContextOptions(user);
return (
<ContentRow
key={user.id}
status={getUserStatus(user)}
data={user}
avatarRole={getUserRole(user)}
avatarSource={user.avatar}
avatarName={user.displayName}
contextOptions={contextOptions}
checked={isUserSelected(selection, user.id)}
onSelect={onContentRowSelect}
style={style}
>
<UserContent user={user} history={history} settings={settings} />
</ContentRow>
);
},
areEqual
);
class SectionBodyContent extends React.PureComponent {
onEmailSentClick = () => {
toastr.success("Context action: Send e-mail");
}
};
onSendMessageClick = () => {
toastr.success("Context action: Send message");
}
};
onEditClick = (user) => {
onEditClick = user => {
const { history, settings } = this.props;
history.push(`${settings.homepage}/edit/${user.userName}`);
}
};
onChangePasswordClick = () => {
toastr.success("Context action: Change password");
}
};
onChangeEmailClick = () => {
toastr.success("Context action: Change e-mail");
}
};
onDisableClick = () => {
toastr.success("Context action: Disable");
}
};
getUserContextOptions = (user) => {
const options = [{
key: "key1",
label: "Send e-mail",
onClick: this.onEmailSentClick
getUserContextOptions = user => {
const options = [
{
key: "key1",
label: "Send e-mail",
onClick: this.onEmailSentClick
},
{
key: "key2",
label: "Send message",
onClick: this.onSendMessageClick
key: "key2",
label: "Send message",
onClick: this.onSendMessageClick
},
{ key: "key3", isSeparator: true },
{
key: "key4",
label: "Edit",
onClick: this.onEditClick.bind(this, user)
key: "key4",
label: "Edit",
onClick: this.onEditClick.bind(this, user)
},
{
key: "key5",
label: "Change password",
onClick: this.onChangePasswordClick
key: "key5",
label: "Change password",
onClick: this.onChangePasswordClick
},
{
key: "key6",
label: "Change e-mail",
onClick: this.onChangeEmailClick
}];
key: "key6",
label: "Change e-mail",
onClick: this.onChangeEmailClick
}
];
return [...options,
!isUserDisabled(user)
return [
...options,
!isUserDisabled(user)
? {
key: "key7",
label: "Disable",
onClick: this.onDisableClick
}
}
: {}
];
};
@ -79,58 +154,46 @@ class SectionBodyContent extends React.PureComponent {
console.log("ContentRow onSelect", checked, user);
if (checked) {
this.props.selectUser(user);
}
else {
} else {
this.props.deselectUser(user);
}
}
};
render() {
console.log("Home SectionBodyContent render()");
const { users, isAdmin, selection, history, settings} = this.props;
const { users, isAdmin, selection, history, settings } = this.props;
return (
<>
{users.map(user => {
const contextOptions = this.getUserContextOptions(user);
return isAdmin ? (
<ContentRow
key={user.id}
status={getUserStatus(user)}
data={user}
avatarRole={getUserRole(user)}
avatarSource={user.avatar}
avatarName={user.displayName}
contextOptions={contextOptions}
checked={isUserSelected(selection, user.id)}
onSelect={this.onContentRowSelect}
>
<UserContent
user={user}
<AutoSizer>
{({ height, width }) => (
<List
className="List"
height={height}
width={width}
itemSize={46} // ContentRow height
itemCount={users.length}
itemData={users}
ref={this.refList}
outerElementType={CustomScrollbarsVirtualList}
>
{({ data, index, style }) => (
<Row
data={data}
index={index}
style={style}
onContentRowSelect={this.onContentRowSelect}
history={history}
settings={settings}
selection={selection}
getUserContextOptions={this.getUserContextOptions}
/>
</ContentRow>
) : (
<ContentRow
key={user.id}
status={getUserStatus(user)}
avatarRole={getUserRole(user)}
avatarSource={user.avatar}
avatarName={user.userName}
>
<UserContent
user={user}
history={history}
settings={settings}
/>
</ContentRow>
);
})}
</>
)}
</List>
)}
</AutoSizer>
);
}
};
}
const mapStateToProps = state => {
return {

View File

@ -110,6 +110,7 @@ class Home extends React.Component {
fontColor={"#999"}
/>
<PageLayout
withBodyScroll={false}
articleHeaderContent={<ArticleHeaderContent />}
articleMainButtonContent={<ArticleMainButtonContent />}
articleBodyContent={<ArticleBodyContent />}

View File

@ -6653,7 +6653,7 @@ mem@^4.0.0:
mimic-fn "^2.0.0"
p-is-promise "^2.0.0"
memoize-one@^5.0.0:
"memoize-one@>=3.1.1 <6", memoize-one@^5.0.0:
version "5.0.5"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.5.tgz#8cd3809555723a07684afafcd6f756072ac75d7e"
integrity sha512-ey6EpYv0tEaIbM/nTDOpHciXUvd+ackQrJgEzBwemhZZIWZjcyodqEcrmqDy2BKRTM3a65kKBV4WtLXJDt26SQ==
@ -8942,6 +8942,19 @@ react-transition-group@^2.3.1, react-transition-group@^2.6.1:
prop-types "^15.6.2"
react-lifecycles-compat "^3.0.4"
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-window@^1.8.5:
version "1.8.5"
resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.5.tgz#a56b39307e79979721021f5d06a67742ecca52d1"
integrity sha512-HeTwlNa37AFa8MDZFZOKcNEkuF2YflA0hpGPiTT9vR7OawEt+GZbfM6wqkBahD3D3pUjIabQYzsnY/BSJbgq6Q==
dependencies:
"@babel/runtime" "^7.0.0"
memoize-one ">=3.1.1 <6"
react@^16.8.6:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"

View File

@ -149,7 +149,7 @@ class PageLayout extends React.PureComponent {
}
{
this.state.isSectionBodyAvailable &&
<SectionBody>{this.state.sectionBodyContent}</SectionBody>
<SectionBody withScroll={this.props.withBodyScroll}>{this.state.sectionBodyContent}</SectionBody>
}
{
this.state.isSectionPagingAvailable &&
@ -177,13 +177,16 @@ PageLayout.propTypes = {
sectionHeaderContent: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
sectionFilterContent: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
sectionBodyContent: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
sectionPagingContent: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node])
sectionPagingContent: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
withBodyScroll: PropTypes.bool
}
PageLayout.defaultProps = {
isBackdropVisible: false,
isArticleVisible: false,
isArticlePinned: false
isArticlePinned: false,
withBodyScroll: true
}
export default PageLayout

View File

@ -1,6 +1,7 @@
import React from 'react'
import styled from 'styled-components'
import Scrollbar from '../../scrollbar'
import React from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import Scrollbar from "../../scrollbar";
const StyledSectionBody = styled.div`
margin: 16px 0;
@ -10,15 +11,24 @@ const StyledSectionBody = styled.div`
const SectionBody = React.memo(props => {
console.log("PageLayout SectionBody render");
const { children } = props;
const { children, withScroll } = props;
return (
<StyledSectionBody>
<Scrollbar stype="mediumBlack">
{children}
</Scrollbar>
{withScroll
? <Scrollbar stype="mediumBlack">{children}</Scrollbar>
: <>{children}</>
}
</StyledSectionBody>
);
});
SectionBody.propTypes = {
withScroll: PropTypes.bool
};
SectionBody.defaultProps = {
withScroll: true
};
export default SectionBody;

View File

@ -2,7 +2,7 @@ import React from 'react'
import { Scrollbars } from 'react-custom-scrollbars';
const Scrollbar = (props) => {
const Scrollbar = React.forwardRef((props, ref) => {
//console.log("Scrollbar render");
const scrollbarType = {
smallWhite: {
@ -37,9 +37,9 @@ const Scrollbar = (props) => {
);
return (
<Scrollbars renderThumbVertical={renderNavThumbVertical} renderThumbHorizontal={renderNavThumbHorizontal} {...props} />
<Scrollbars renderThumbVertical={renderNavThumbVertical} renderThumbHorizontal={renderNavThumbHorizontal} {...props} ref={ref} />
);
}
});
Scrollbar.defaultProps = {
stype: "smallBlack"