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:
parent
f308c669e1
commit
b0d5d1ea72
@ -26,6 +26,8 @@
|
|||||||
"react-router": "5.0.1",
|
"react-router": "5.0.1",
|
||||||
"react-router-dom": "5.0.1",
|
"react-router-dom": "5.0.1",
|
||||||
"react-scripts": "3.0.1",
|
"react-scripts": "3.0.1",
|
||||||
|
"react-virtualized-auto-sizer": "^1.0.2",
|
||||||
|
"react-window": "^1.8.5",
|
||||||
"reactstrap": "8.0.0",
|
"reactstrap": "8.0.0",
|
||||||
"redux": "4.0.1",
|
"redux": "4.0.1",
|
||||||
"redux-form": "^8.2.4",
|
"redux-form": "^8.2.4",
|
||||||
|
@ -1,76 +1,151 @@
|
|||||||
import React from "react";
|
import React, { memo, useCallback } from "react";
|
||||||
import { withRouter } from "react-router";
|
import { withRouter } from "react-router";
|
||||||
import { connect } from "react-redux";
|
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 UserContent from "./userContent";
|
||||||
//import config from "../../../../../../package.json";
|
//import config from "../../../../../../package.json";
|
||||||
import { selectUser, deselectUser, setSelection } from "../../../../../store/people/actions";
|
import {
|
||||||
import { isUserSelected, getUserStatus, getUserRole, isUserDisabled } from '../../../../../store/people/selectors';
|
selectUser,
|
||||||
import { isAdmin } from '../../../../../store/auth/selectors';
|
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 {
|
class SectionBodyContent extends React.PureComponent {
|
||||||
|
|
||||||
onEmailSentClick = () => {
|
onEmailSentClick = () => {
|
||||||
toastr.success("Context action: Send e-mail");
|
toastr.success("Context action: Send e-mail");
|
||||||
}
|
};
|
||||||
|
|
||||||
onSendMessageClick = () => {
|
onSendMessageClick = () => {
|
||||||
toastr.success("Context action: Send message");
|
toastr.success("Context action: Send message");
|
||||||
}
|
};
|
||||||
|
|
||||||
onEditClick = (user) => {
|
onEditClick = user => {
|
||||||
const { history, settings } = this.props;
|
const { history, settings } = this.props;
|
||||||
history.push(`${settings.homepage}/edit/${user.userName}`);
|
history.push(`${settings.homepage}/edit/${user.userName}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
onChangePasswordClick = () => {
|
onChangePasswordClick = () => {
|
||||||
toastr.success("Context action: Change password");
|
toastr.success("Context action: Change password");
|
||||||
}
|
};
|
||||||
|
|
||||||
onChangeEmailClick = () => {
|
onChangeEmailClick = () => {
|
||||||
toastr.success("Context action: Change e-mail");
|
toastr.success("Context action: Change e-mail");
|
||||||
}
|
};
|
||||||
|
|
||||||
onDisableClick = () => {
|
onDisableClick = () => {
|
||||||
toastr.success("Context action: Disable");
|
toastr.success("Context action: Disable");
|
||||||
}
|
};
|
||||||
|
|
||||||
getUserContextOptions = (user) => {
|
getUserContextOptions = user => {
|
||||||
|
const options = [
|
||||||
const options = [{
|
{
|
||||||
key: "key1",
|
key: "key1",
|
||||||
label: "Send e-mail",
|
label: "Send e-mail",
|
||||||
onClick: this.onEmailSentClick
|
onClick: this.onEmailSentClick
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "key2",
|
key: "key2",
|
||||||
label: "Send message",
|
label: "Send message",
|
||||||
onClick: this.onSendMessageClick
|
onClick: this.onSendMessageClick
|
||||||
},
|
},
|
||||||
{ key: "key3", isSeparator: true },
|
{ key: "key3", isSeparator: true },
|
||||||
{
|
{
|
||||||
key: "key4",
|
key: "key4",
|
||||||
label: "Edit",
|
label: "Edit",
|
||||||
onClick: this.onEditClick.bind(this, user)
|
onClick: this.onEditClick.bind(this, user)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "key5",
|
key: "key5",
|
||||||
label: "Change password",
|
label: "Change password",
|
||||||
onClick: this.onChangePasswordClick
|
onClick: this.onChangePasswordClick
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "key6",
|
key: "key6",
|
||||||
label: "Change e-mail",
|
label: "Change e-mail",
|
||||||
onClick: this.onChangeEmailClick
|
onClick: this.onChangeEmailClick
|
||||||
}];
|
}
|
||||||
|
];
|
||||||
|
|
||||||
return [...options,
|
return [
|
||||||
!isUserDisabled(user)
|
...options,
|
||||||
|
!isUserDisabled(user)
|
||||||
? {
|
? {
|
||||||
key: "key7",
|
key: "key7",
|
||||||
label: "Disable",
|
label: "Disable",
|
||||||
onClick: this.onDisableClick
|
onClick: this.onDisableClick
|
||||||
}
|
}
|
||||||
: {}
|
: {}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@ -79,58 +154,46 @@ class SectionBodyContent extends React.PureComponent {
|
|||||||
console.log("ContentRow onSelect", checked, user);
|
console.log("ContentRow onSelect", checked, user);
|
||||||
if (checked) {
|
if (checked) {
|
||||||
this.props.selectUser(user);
|
this.props.selectUser(user);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.props.deselectUser(user);
|
this.props.deselectUser(user);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.log("Home SectionBodyContent render()");
|
console.log("Home SectionBodyContent render()");
|
||||||
const { users, isAdmin, selection, history, settings} = this.props;
|
const { users, isAdmin, selection, history, settings } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<AutoSizer>
|
||||||
{users.map(user => {
|
{({ height, width }) => (
|
||||||
const contextOptions = this.getUserContextOptions(user);
|
<List
|
||||||
return isAdmin ? (
|
className="List"
|
||||||
<ContentRow
|
height={height}
|
||||||
key={user.id}
|
width={width}
|
||||||
status={getUserStatus(user)}
|
itemSize={46} // ContentRow height
|
||||||
data={user}
|
itemCount={users.length}
|
||||||
avatarRole={getUserRole(user)}
|
itemData={users}
|
||||||
avatarSource={user.avatar}
|
ref={this.refList}
|
||||||
avatarName={user.displayName}
|
outerElementType={CustomScrollbarsVirtualList}
|
||||||
contextOptions={contextOptions}
|
>
|
||||||
checked={isUserSelected(selection, user.id)}
|
{({ data, index, style }) => (
|
||||||
onSelect={this.onContentRowSelect}
|
<Row
|
||||||
>
|
data={data}
|
||||||
<UserContent
|
index={index}
|
||||||
user={user}
|
style={style}
|
||||||
|
onContentRowSelect={this.onContentRowSelect}
|
||||||
history={history}
|
history={history}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
|
selection={selection}
|
||||||
|
getUserContextOptions={this.getUserContextOptions}
|
||||||
/>
|
/>
|
||||||
</ContentRow>
|
)}
|
||||||
) : (
|
</List>
|
||||||
<ContentRow
|
)}
|
||||||
key={user.id}
|
</AutoSizer>
|
||||||
status={getUserStatus(user)}
|
|
||||||
avatarRole={getUserRole(user)}
|
|
||||||
avatarSource={user.avatar}
|
|
||||||
avatarName={user.userName}
|
|
||||||
>
|
|
||||||
<UserContent
|
|
||||||
user={user}
|
|
||||||
history={history}
|
|
||||||
settings={settings}
|
|
||||||
/>
|
|
||||||
</ContentRow>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
|
@ -110,6 +110,7 @@ class Home extends React.Component {
|
|||||||
fontColor={"#999"}
|
fontColor={"#999"}
|
||||||
/>
|
/>
|
||||||
<PageLayout
|
<PageLayout
|
||||||
|
withBodyScroll={false}
|
||||||
articleHeaderContent={<ArticleHeaderContent />}
|
articleHeaderContent={<ArticleHeaderContent />}
|
||||||
articleMainButtonContent={<ArticleMainButtonContent />}
|
articleMainButtonContent={<ArticleMainButtonContent />}
|
||||||
articleBodyContent={<ArticleBodyContent />}
|
articleBodyContent={<ArticleBodyContent />}
|
||||||
|
@ -6653,7 +6653,7 @@ mem@^4.0.0:
|
|||||||
mimic-fn "^2.0.0"
|
mimic-fn "^2.0.0"
|
||||||
p-is-promise "^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"
|
version "5.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.5.tgz#8cd3809555723a07684afafcd6f756072ac75d7e"
|
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.5.tgz#8cd3809555723a07684afafcd6f756072ac75d7e"
|
||||||
integrity sha512-ey6EpYv0tEaIbM/nTDOpHciXUvd+ackQrJgEzBwemhZZIWZjcyodqEcrmqDy2BKRTM3a65kKBV4WtLXJDt26SQ==
|
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"
|
prop-types "^15.6.2"
|
||||||
react-lifecycles-compat "^3.0.4"
|
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:
|
react@^16.8.6:
|
||||||
version "16.8.6"
|
version "16.8.6"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"
|
resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"
|
||||||
|
@ -149,7 +149,7 @@ class PageLayout extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
this.state.isSectionBodyAvailable &&
|
this.state.isSectionBodyAvailable &&
|
||||||
<SectionBody>{this.state.sectionBodyContent}</SectionBody>
|
<SectionBody withScroll={this.props.withBodyScroll}>{this.state.sectionBodyContent}</SectionBody>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
this.state.isSectionPagingAvailable &&
|
this.state.isSectionPagingAvailable &&
|
||||||
@ -177,13 +177,16 @@ PageLayout.propTypes = {
|
|||||||
sectionHeaderContent: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
|
sectionHeaderContent: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
|
||||||
sectionFilterContent: 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]),
|
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 = {
|
PageLayout.defaultProps = {
|
||||||
isBackdropVisible: false,
|
isBackdropVisible: false,
|
||||||
isArticleVisible: false,
|
isArticleVisible: false,
|
||||||
isArticlePinned: false
|
isArticlePinned: false,
|
||||||
|
withBodyScroll: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PageLayout
|
export default PageLayout
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react'
|
import React from "react";
|
||||||
import styled from 'styled-components'
|
import PropTypes from "prop-types";
|
||||||
import Scrollbar from '../../scrollbar'
|
import styled from "styled-components";
|
||||||
|
import Scrollbar from "../../scrollbar";
|
||||||
|
|
||||||
const StyledSectionBody = styled.div`
|
const StyledSectionBody = styled.div`
|
||||||
margin: 16px 0;
|
margin: 16px 0;
|
||||||
@ -8,17 +9,26 @@ const StyledSectionBody = styled.div`
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const SectionBody = React.memo(props => {
|
const SectionBody = React.memo(props => {
|
||||||
console.log("PageLayout SectionBody render");
|
console.log("PageLayout SectionBody render");
|
||||||
const { children } = props;
|
const { children, withScroll } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledSectionBody>
|
<StyledSectionBody>
|
||||||
<Scrollbar stype="mediumBlack">
|
{withScroll
|
||||||
{children}
|
? <Scrollbar stype="mediumBlack">{children}</Scrollbar>
|
||||||
</Scrollbar>
|
: <>{children}</>
|
||||||
|
}
|
||||||
</StyledSectionBody>
|
</StyledSectionBody>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default SectionBody;
|
SectionBody.propTypes = {
|
||||||
|
withScroll: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
|
SectionBody.defaultProps = {
|
||||||
|
withScroll: true
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SectionBody;
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||||||
import { Scrollbars } from 'react-custom-scrollbars';
|
import { Scrollbars } from 'react-custom-scrollbars';
|
||||||
|
|
||||||
|
|
||||||
const Scrollbar = (props) => {
|
const Scrollbar = React.forwardRef((props, ref) => {
|
||||||
//console.log("Scrollbar render");
|
//console.log("Scrollbar render");
|
||||||
const scrollbarType = {
|
const scrollbarType = {
|
||||||
smallWhite: {
|
smallWhite: {
|
||||||
@ -37,9 +37,9 @@ const Scrollbar = (props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Scrollbars renderThumbVertical={renderNavThumbVertical} renderThumbHorizontal={renderNavThumbHorizontal} {...props} />
|
<Scrollbars renderThumbVertical={renderNavThumbVertical} renderThumbHorizontal={renderNavThumbHorizontal} {...props} ref={ref} />
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
Scrollbar.defaultProps = {
|
Scrollbar.defaultProps = {
|
||||||
stype: "smallBlack"
|
stype: "smallBlack"
|
||||||
|
Loading…
Reference in New Issue
Block a user