diff --git a/frontend/src/components/common/notification-popover/index.css b/frontend/src/components/common/notification-popover/index.css index a4d9cd6f06..8fdb2af036 100644 --- a/frontend/src/components/common/notification-popover/index.css +++ b/frontend/src/components/common/notification-popover/index.css @@ -198,6 +198,10 @@ color: #ED7109; } +.notification-container .nav-indicator-container::before { + bottom: 0px; +} + @media (max-width: 768px) { .notification-container { right: -60px; diff --git a/frontend/src/components/common/notification-popover/index.js b/frontend/src/components/common/notification-popover/index.js index b9d864bbf1..c6a6f0c8e1 100644 --- a/frontend/src/components/common/notification-popover/index.js +++ b/frontend/src/components/common/notification-popover/index.js @@ -3,13 +3,20 @@ import PropTypes from 'prop-types'; import { Popover } from 'reactstrap'; import { gettext } from '../../../utils/constants'; import SeahubModalCloseIcon from '../seahub-modal-close'; +import { NAV_ITEM_MARGIN } from '../../../constants'; import './index.css'; class NotificationPopover extends React.Component { + constructor(props) { + super(props); + this.itemRefs = []; + } + componentDidMount() { document.addEventListener('mousedown', this.handleOutsideClick, true); + this.forceUpdate(); } componentWillUnmount() { @@ -39,6 +46,11 @@ class NotificationPopover extends React.Component { render() { const { headerText = '', bodyText = '', footerText = '', currentTab, generalNoticeListUnseen, discussionNoticeListUnseen } = this.props; + const activeIndex = currentTab === 'general' ? 0 : 1; + const itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); + const indicatorWidth = itemWidths[activeIndex]; + const indicatorOffset = itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0) + (2 * activeIndex + 1) * NAV_ITEM_MARGIN; + return (
-
    -
  • this.tabItemClick('general')}> - +
      +
    • this.itemRefs[0] = el} + onClick={() => this.tabItemClick('general')} + > + {gettext('General')} {generalNoticeListUnseen > 0 && ({generalNoticeListUnseen})}
    • -
    • this.tabItemClick('discussion')}> - +
    • this.itemRefs[1] = el} + onClick={() => this.tabItemClick('discussion')} + > + {gettext('Discussion')} {discussionNoticeListUnseen > 0 && ({discussionNoticeListUnseen})} diff --git a/frontend/src/constants/index.js b/frontend/src/constants/index.js index e692bc0efd..116c8854ea 100644 --- a/frontend/src/constants/index.js +++ b/frontend/src/constants/index.js @@ -75,3 +75,5 @@ export const SEARCH_FILTER_BY_DATE_TYPE_KEY = { }; export const SEARCH_FILTERS_SHOW_KEY = 'search_filters_show'; + +export const NAV_ITEM_MARGIN = 12; diff --git a/frontend/src/css/layout.css b/frontend/src/css/layout.css index d227b2b4f5..25722dab11 100644 --- a/frontend/src/css/layout.css +++ b/frontend/src/css/layout.css @@ -356,14 +356,19 @@ img[src=""],img:not([src]) { /* for first loading img*/ color: #666; } +.nav-indicator-container .nav-item { + min-width: fit-content; +} + .nav-indicator-container .nav-item .nav-link { border: none; + min-width: fit-content; } .nav-indicator-container::before { content: ''; position: absolute; - bottom: 0; + bottom: -2px; height: 2px; width: var(--indicator-width); background: #ED7109; diff --git a/frontend/src/pages/dashboard/files-activities.js b/frontend/src/pages/dashboard/files-activities.js index ec4ed53969..49f21c0ee1 100644 --- a/frontend/src/pages/dashboard/files-activities.js +++ b/frontend/src/pages/dashboard/files-activities.js @@ -13,6 +13,8 @@ import '../../css/files-activities.css'; dayjs.locale(window.app.config.lang); +const NAV_ITEM_MARGIN = 16; + class FilesActivities extends Component { constructor(props) { @@ -34,7 +36,6 @@ class FilesActivities extends Component { this.oldPathList = []; this.availableUserEmails = new Set(); this.itemRefs = []; - this.itemWidths = []; } componentDidMount() { @@ -80,24 +81,13 @@ class FilesActivities extends Component { this.setState({ onlyMine: isMyActivities }); }); - this.measureItems(); - } - - componentDidUpdate(prevProps, prevState) { - if (this.state.onlyMine !== prevState.onlyMine) { - this.measureItems(); - } + this.forceUpdate(); } componentWillUnmount() { this.unlisten && this.unlisten(); } - measureItems = () => { - this.itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); - this.forceUpdate(); - }; - mergePublishEvents = (events) => { events.forEach((item) => { if (item.op_type === 'publish') { @@ -255,8 +245,9 @@ class FilesActivities extends Component { render() { const { targetUsers, availableUsers, onlyMine } = this.state; const activeIndex = onlyMine ? 1 : 0; - const indicatorWidth = this.itemWidths[activeIndex] || 78; - const indicatorOffset = this.itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0); + const itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); + const indicatorWidth = itemWidths[activeIndex]; + const indicatorOffset = itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0) + (2 * activeIndex + 1) * NAV_ITEM_MARGIN; return (
      @@ -268,10 +259,10 @@ class FilesActivities extends Component { '--indicator-offset': `${indicatorOffset}px` }} > -
    • this.itemRefs[0] = el}> +
    • this.itemRefs[0] = el}> {gettext('All Activities')}
    • -
    • this.itemRefs[1] = el}> +
    • this.itemRefs[1] = el}> {gettext('My Activities')}
    diff --git a/frontend/src/pages/sys-admin/admin-logs/logs-nav.js b/frontend/src/pages/sys-admin/admin-logs/logs-nav.js index 0c125817e8..db7d15d44c 100644 --- a/frontend/src/pages/sys-admin/admin-logs/logs-nav.js +++ b/frontend/src/pages/sys-admin/admin-logs/logs-nav.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from '@gatsbyjs/reach-router'; import { siteRoot, gettext } from '../../../utils/constants'; +import { NAV_ITEM_MARGIN } from '../../../constants'; const propTypes = { currentItem: PropTypes.string.isRequired @@ -16,18 +17,18 @@ class LogsNav extends React.Component { { name: 'login', urlPart: 'admin-logs/login', text: gettext('Admin Login Logs') }, ]; this.itemRefs = []; - this.itemWidths = []; } componentDidMount() { - this.itemWidths = this.itemRefs.map(ref => ref?.offsetWidth || 0); + this.forceUpdate(); } render() { const { currentItem } = this.props; const activeIndex = this.navItems.findIndex(item => item.name === currentItem) || 0; - const indicatorWidth = this.itemWidths[activeIndex] || 167; - const indicatorOffset = this.itemWidths.slice(0, activeIndex).reduce((prev, cur) => prev + cur, 0); + const itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); + const indicatorWidth = itemWidths[activeIndex]; + const indicatorOffset = itemWidths.slice(0, activeIndex).reduce((prev, cur) => prev + cur, 0) + (2 * activeIndex + 1) * NAV_ITEM_MARGIN; return (
      { return (
    • this.itemRefs[index] = el} > - {item.text} + {item.text}
    • ); })} diff --git a/frontend/src/pages/sys-admin/devices/devices-nav.js b/frontend/src/pages/sys-admin/devices/devices-nav.js index 11bbbee125..5a08a303c0 100644 --- a/frontend/src/pages/sys-admin/devices/devices-nav.js +++ b/frontend/src/pages/sys-admin/devices/devices-nav.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from '@gatsbyjs/reach-router'; import { siteRoot, gettext, isPro } from '../../../utils/constants'; +import { NAV_ITEM_MARGIN } from '../../../constants'; const propTypes = { currentItem: PropTypes.string.isRequired @@ -19,22 +20,18 @@ class Nav extends React.Component { this.navItems.push({ name: 'errors', urlPart: 'errors', text: gettext('Errors') }); } this.itemRefs = []; - this.itemWidths = []; } componentDidMount() { - this.measureItems(); + this.forceUpdate(); } - measureItems = () => { - this.itemWidths = this.itemRefs.map(ref => ref?.offsetWidth || 77); - }; - render() { const { currentItem } = this.props; const activeIndex = this.navItems.findIndex(item => item.name === currentItem) || 0; - const indicatorWidth = this.itemWidths[activeIndex] || 77; - const indicatorOffset = this.itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0); + const itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); + const indicatorWidth = itemWidths[activeIndex]; + const indicatorOffset = itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0) + (2 * activeIndex + 1) * NAV_ITEM_MARGIN; return (
      @@ -48,11 +45,11 @@ class Nav extends React.Component { {this.navItems.map((item, index) => { return (
    • this.itemRefs[index] = el} > - {item.text} + {item.text}
    • ); })} diff --git a/frontend/src/pages/sys-admin/links/links-nav.js b/frontend/src/pages/sys-admin/links/links-nav.js index 13f0b6cb64..115976820a 100644 --- a/frontend/src/pages/sys-admin/links/links-nav.js +++ b/frontend/src/pages/sys-admin/links/links-nav.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { Link } from '@gatsbyjs/reach-router'; import { siteRoot, gettext } from '../../../utils/constants'; import SortMenu from '../../../components/sort-menu'; +import { NAV_ITEM_MARGIN } from '../../../constants'; const propTypes = { currentItem: PropTypes.string.isRequired, @@ -27,11 +28,10 @@ class Nav extends React.Component { { value: 'view_cnt-desc', text: gettext('Descending by visit count') } ]; this.itemRefs = []; - this.itemWidths = []; } componentDidMount() { - this.itemWidths = this.itemRefs.map(ref => ref?.offsetWidth || 98); + this.forceUpdate(); } onSelectSortOption = (item) => { @@ -43,8 +43,9 @@ class Nav extends React.Component { const { currentItem, sortBy, sortOrder } = this.props; const showSortIcon = currentItem == 'shareLinks'; const activeIndex = this.navItems.findIndex(item => item.name === currentItem) || 0; - const indicatorWidth = this.itemWidths[activeIndex] || 98; - const indicatorOffset = this.itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0); + const itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); + const indicatorWidth = itemWidths[activeIndex]; + const indicatorOffset = itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0) + (2 * activeIndex + 1) * NAV_ITEM_MARGIN; return (
        { return (
      • this.itemRefs[index] = el} > - {item.text} + {item.text}
      • ); })} diff --git a/frontend/src/pages/sys-admin/logs-page/logs-nav.js b/frontend/src/pages/sys-admin/logs-page/logs-nav.js index 9d3ddc57dd..59a782f6e9 100644 --- a/frontend/src/pages/sys-admin/logs-page/logs-nav.js +++ b/frontend/src/pages/sys-admin/logs-page/logs-nav.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from '@gatsbyjs/reach-router'; import { siteRoot, gettext } from '../../../utils/constants'; +import { NAV_ITEM_MARGIN } from '../../../constants'; const propTypes = { currentItem: PropTypes.string.isRequired @@ -20,18 +21,18 @@ class Nav extends React.Component { { name: 'groupMember', urlPart: 'logs/group-member-audit', text: gettext('Group Member') }, ]; this.itemRefs = []; - this.itemWidths = []; } componentDidMount() { - this.itemWidths = this.itemRefs.map(ref => ref?.offsetWidth) || 59; + this.forceUpdate(); } render() { const { currentItem } = this.props; const activeIndex = this.navItems.findIndex(item => item.name === currentItem) || 0; - const indicatorWidth = this.itemWidths[activeIndex] || 59; - const leftOffset = this.itemWidths.slice(0, activeIndex).reduce((prev, cur) => prev + cur, 0); + const itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); + const indicatorWidth = itemWidths[activeIndex]; + const leftOffset = itemWidths.slice(0, activeIndex).reduce((prev, cur) => prev + cur, 0) + (2 * activeIndex + 1) * NAV_ITEM_MARGIN; return (
          { return (
        • this.itemRefs[index] = el} > - {item.text} + {item.text}
        • ); })} diff --git a/frontend/src/pages/sys-admin/repos/repos-nav.js b/frontend/src/pages/sys-admin/repos/repos-nav.js index 8bb07100db..3b28ec3dce 100644 --- a/frontend/src/pages/sys-admin/repos/repos-nav.js +++ b/frontend/src/pages/sys-admin/repos/repos-nav.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { Link } from '@gatsbyjs/reach-router'; import { siteRoot, gettext } from '../../../utils/constants'; import SortMenu from '../../../components/sort-menu'; +import { NAV_ITEM_MARGIN } from '../../../constants'; const propTypes = { currentItem: PropTypes.string.isRequired, @@ -25,17 +26,10 @@ class Nav extends React.Component { { value: 'size-desc', text: gettext('Descending by size') } ]; this.itemRefs = []; - this.itemWidths = []; } componentDidMount() { - this.measureItems(); - } - - componentDidUpdate(prevProps) { - if (this.props.currentItem !== prevProps.currentItem) { - this.measureItems(); - } + this.forceUpdate(); } onSelectSortOption = (item) => { @@ -43,17 +37,13 @@ class Nav extends React.Component { this.props.sortItems(sortBy); }; - measureItems = () => { - this.itemWidths = this.itemRefs.map(ref => ref?.offsetWidth || 77); - this.forceUpdate(); - }; - render() { const { currentItem, sortBy, sortOrder = 'desc' } = this.props; const showSortIcon = currentItem == 'all-libraries' || currentItem == 'all-wikis'; const activeIndex = this.navItems.findIndex(item => item.name === currentItem) || 0; - const indicatorWidth = this.itemWidths[activeIndex] || 56; - const indicatorOffset = this.itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0); + const itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); + const indicatorWidth = itemWidths[activeIndex]; + const indicatorOffset = itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0) + (2 * activeIndex + 1) * NAV_ITEM_MARGIN; return (
          @@ -67,11 +57,11 @@ class Nav extends React.Component { {this.navItems.map((item, index) => { return (
        • this.itemRefs[index] = el} > - {item.text} + {item.text}
        • ); })} diff --git a/frontend/src/pages/sys-admin/statistic/statistic-nav.js b/frontend/src/pages/sys-admin/statistic/statistic-nav.js index 2e77d86f63..7d0c0cfa84 100644 --- a/frontend/src/pages/sys-admin/statistic/statistic-nav.js +++ b/frontend/src/pages/sys-admin/statistic/statistic-nav.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from '@gatsbyjs/reach-router'; import { siteRoot, gettext } from '../../../utils/constants'; +import { NAV_ITEM_MARGIN } from '../../../constants'; const propTypes = { currentItem: PropTypes.string.isRequired @@ -20,22 +21,18 @@ class Nav extends React.Component { { name: 'metricsStatistic', urlPart: 'statistics/metrics', text: gettext('Metrics') }, ]; this.itemRefs = []; - this.itemWidths = []; } componentDidMount() { - this.measureItems(); + this.forceUpdate(); } - measureItems = () => { - this.itemWidths = this.itemRefs.map(ref => ref?.offsetWidth || 0); - }; - render() { const { currentItem } = this.props; const activeIndex = this.navItems.findIndex(item => item.name === currentItem); - const indicatorWidth = this.itemWidths[activeIndex] || 56; - const indicatorOffset = this.itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0); + const itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); + const indicatorWidth = itemWidths[activeIndex]; + const indicatorOffset = itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0) + (2 * activeIndex + 1) * NAV_ITEM_MARGIN; return (
          @@ -49,11 +46,11 @@ class Nav extends React.Component { {this.navItems.map((item, index) => { return (
        • this.itemRefs[index] = el} > - {item.text} + {item.text}
        • ); })} diff --git a/frontend/src/pages/sys-admin/users/user-nav.js b/frontend/src/pages/sys-admin/users/user-nav.js index fdf6b7629f..89ff9dd4c0 100644 --- a/frontend/src/pages/sys-admin/users/user-nav.js +++ b/frontend/src/pages/sys-admin/users/user-nav.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from '@gatsbyjs/reach-router'; import { siteRoot, gettext } from '../../../utils/constants'; +import { NAV_ITEM_MARGIN } from '../../../constants'; const propTypes = { email: PropTypes.string, @@ -21,22 +22,18 @@ class Nav extends React.Component { { name: 'groups', urlPart: 'groups', text: gettext('Groups') } ]; this.itemRefs = []; - this.itemWidths = []; } componentDidMount() { - this.measureItems(); + this.forceUpdate(); } - measureItems = () => { - this.itemWidths = this.itemRefs.map(ref => ref?.offsetWidth || 77); - }; - render() { const { currentItem, email, userName } = this.props; const activeIndex = this.navItems.findIndex(item => item.name === currentItem) || 0; - const indicatorWidth = this.itemWidths[activeIndex] || 56; - const indicatorOffset = this.itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0); + const itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); + const indicatorWidth = itemWidths[activeIndex]; + const indicatorOffset = itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0) + (2 * activeIndex + 1) * NAV_ITEM_MARGIN; return (
          @@ -52,11 +49,11 @@ class Nav extends React.Component { {this.navItems.map((item, index) => { return (
        • this.itemRefs[index] = el} > - {item.text} + {item.text}
        • ); })} diff --git a/frontend/src/pages/sys-admin/users/users-nav.js b/frontend/src/pages/sys-admin/users/users-nav.js index 5e41b9743e..9059a04da5 100644 --- a/frontend/src/pages/sys-admin/users/users-nav.js +++ b/frontend/src/pages/sys-admin/users/users-nav.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { Link } from '@gatsbyjs/reach-router'; import { siteRoot, gettext, haveLDAP, isDefaultAdmin } from '../../../utils/constants'; import SortMenu from '../../../components/sort-menu'; +import { NAV_ITEM_MARGIN } from '../../../constants'; const propTypes = { currentItem: PropTypes.string.isRequired, @@ -35,11 +36,10 @@ class Nav extends React.Component { ]; this.itemRefs = []; - this.itemWidths = []; } componentDidMount() { - this.measureItems(); + this.forceUpdate(); } onSelectSortOption = (item) => { @@ -47,16 +47,13 @@ class Nav extends React.Component { this.props.sortItems(sortBy, sortOrder); }; - measureItems = () => { - this.itemWidths = this.itemRefs.map(ref => ref?.offsetWidth || 77); - }; - render() { const { currentItem, sortBy, sortOrder } = this.props; const showSortIcon = currentItem == 'database' || currentItem == 'ldap-imported'; const activeIndex = this.navItems.findIndex(item => item.name === currentItem) || 0; - const indicatorWidth = this.itemWidths[activeIndex] || 85; - const indicatorOffset = this.itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0); + const itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); + const indicatorWidth = itemWidths[activeIndex]; + const indicatorOffset = itemWidths.slice(0, activeIndex).reduce((a, b) => a + b, 0) + (2 * activeIndex + 1) * NAV_ITEM_MARGIN; return (
          @@ -70,11 +67,11 @@ class Nav extends React.Component { {this.navItems.map((item, index) => { return (
        • this.itemRefs[index] = el} > - {item.text} + {item.text}
        • ); })} diff --git a/frontend/src/pages/sys-admin/virus-scan/nav.js b/frontend/src/pages/sys-admin/virus-scan/nav.js index e7c02ce757..c95fbb1724 100644 --- a/frontend/src/pages/sys-admin/virus-scan/nav.js +++ b/frontend/src/pages/sys-admin/virus-scan/nav.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from '@gatsbyjs/reach-router'; import { siteRoot, gettext } from '../../../utils/constants'; +import { NAV_ITEM_MARGIN } from '../../../constants'; const propTypes = { currentItem: PropTypes.string.isRequired @@ -16,18 +17,18 @@ class Nav extends React.Component { { name: 'unhandled', urlPart: 'unhandled', text: gettext('Unhandled') } ]; this.itemRefs = []; - this.itemWidths = []; } componentDidMount() { - this.itemWidths = this.itemRefs.map(ref => ref?.offsetWidth) || 59; + this.forceUpdate(); } render() { const { currentItem } = this.props; const activeIndex = this.navItems.findIndex(item => item.name === currentItem) || 0; - const indicatorWidth = this.itemWidths[activeIndex] || 59; - const indicatorOffset = this.itemWidths.slice(0, activeIndex).reduce((prev, cur) => prev + cur, 0); + const itemWidths = this.itemRefs.map(ref => ref?.offsetWidth); + const indicatorWidth = itemWidths[activeIndex]; + const indicatorOffset = itemWidths.slice(0, activeIndex).reduce((prev, cur) => prev + cur, 0) + (2 * activeIndex + 1) * NAV_ITEM_MARGIN; return (
            { return (
          • this.itemRefs[index] = el} > - {item.text} + {item.text}
          • ); })}