diff --git a/products/ASC.People/Client/package.json b/products/ASC.People/Client/package.json index a9c3f0beb9..3db9a987e4 100644 --- a/products/ASC.People/Client/package.json +++ b/products/ASC.People/Client/package.json @@ -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", diff --git a/products/ASC.People/Client/src/components/pages/Home/Section/Body/index.js b/products/ASC.People/Client/src/components/pages/Home/Section/Body/index.js index 18c4140b7f..4f3c0a9b7a 100644 --- a/products/ASC.People/Client/src/components/pages/Home/Section/Body/index.js +++ b/products/ASC.People/Client/src/components/pages/Home/Section/Body/index.js @@ -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 ( + + {children} + + ); +}; + +const CustomScrollbarsVirtualList = React.forwardRef((props, 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 ( + + + + ); + }, + 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 ? ( - - + {({ height, width }) => ( + + {({ data, index, style }) => ( + - - ) : ( - - - - ); - })} - + )} + + )} + ); } -}; +} const mapStateToProps = state => { return { diff --git a/products/ASC.People/Client/src/components/pages/Home/index.js b/products/ASC.People/Client/src/components/pages/Home/index.js index 8cf452bf91..6f0895e1db 100644 --- a/products/ASC.People/Client/src/components/pages/Home/index.js +++ b/products/ASC.People/Client/src/components/pages/Home/index.js @@ -110,6 +110,7 @@ class Home extends React.Component { fontColor={"#999"} /> } articleMainButtonContent={} articleBodyContent={} diff --git a/products/ASC.People/Client/yarn.lock b/products/ASC.People/Client/yarn.lock index 52917118d9..afc25a66d6 100644 --- a/products/ASC.People/Client/yarn.lock +++ b/products/ASC.People/Client/yarn.lock @@ -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" diff --git a/web/ASC.Web.Components/src/components/page-layout/index.js b/web/ASC.Web.Components/src/components/page-layout/index.js index 48b433ffa6..44ae96afb2 100644 --- a/web/ASC.Web.Components/src/components/page-layout/index.js +++ b/web/ASC.Web.Components/src/components/page-layout/index.js @@ -149,7 +149,7 @@ class PageLayout extends React.PureComponent { } { this.state.isSectionBodyAvailable && - {this.state.sectionBodyContent} + {this.state.sectionBodyContent} } { 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 \ No newline at end of file diff --git a/web/ASC.Web.Components/src/components/page-layout/sub-comoponents/section-body.js b/web/ASC.Web.Components/src/components/page-layout/sub-comoponents/section-body.js index 4ccd05ca57..75703a634d 100644 --- a/web/ASC.Web.Components/src/components/page-layout/sub-comoponents/section-body.js +++ b/web/ASC.Web.Components/src/components/page-layout/sub-comoponents/section-body.js @@ -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; @@ -8,17 +9,26 @@ const StyledSectionBody = styled.div` flex-grow: 1; `; -const SectionBody = React.memo(props => { +const SectionBody = React.memo(props => { console.log("PageLayout SectionBody render"); - const { children } = props; + const { children, withScroll } = props; return ( - - {children} - + {withScroll + ? {children} + : <>{children} + } ); }); -export default SectionBody; \ No newline at end of file +SectionBody.propTypes = { + withScroll: PropTypes.bool +}; + +SectionBody.defaultProps = { + withScroll: true +}; + +export default SectionBody; diff --git a/web/ASC.Web.Components/src/components/scrollbar/index.js b/web/ASC.Web.Components/src/components/scrollbar/index.js index c40ddfd4a6..d1752b0bba 100644 --- a/web/ASC.Web.Components/src/components/scrollbar/index.js +++ b/web/ASC.Web.Components/src/components/scrollbar/index.js @@ -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 ( - + ); -} +}); Scrollbar.defaultProps = { stype: "smallBlack"