diff --git a/frontend/src/components/dialog/trash-dialog.js b/frontend/src/components/dialog/trash-dialog.js index 2e8fee63d7..de5851e7ce 100644 --- a/frontend/src/components/dialog/trash-dialog.js +++ b/frontend/src/components/dialog/trash-dialog.js @@ -4,7 +4,7 @@ import { navigate } from '@gatsbyjs/reach-router'; import { Modal, ModalHeader, ModalBody } from 'reactstrap'; import moment from 'moment'; import { Utils } from '../../utils/utils'; -import {gettext, siteRoot, enableUserCleanTrash, username} from '../../utils/constants'; +import { gettext, siteRoot, enableUserCleanTrash, username } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { repotrashAPI } from '../../utils/repo-trash-api'; import ModalPortal from '../../components/modal-portal'; @@ -65,7 +65,7 @@ class TrashDialog extends React.Component { onSearchedClick = (selectedItem) => { if (selectedItem.is_dir === true) { let url = siteRoot + 'library/' + selectedItem.repo_id + '/' + selectedItem.repo_name + selectedItem.path; - navigate(url, {repalce: true}); + navigate(url, { repalce: true }); } else { let url = siteRoot + 'lib/' + selectedItem.repo_id + '/file' + Utils.encodePath(selectedItem.path); let newWindow = window.open('about:blank'); @@ -161,7 +161,7 @@ class TrashDialog extends React.Component { } > -
+
{ - this.setState({isIconShown: true}); + this.setState({ isIconShown: true }); }; handleMouseOut = () => { - this.setState({isIconShown: false}); + this.setState({ isIconShown: false }); }; restoreItem = (e) => { @@ -374,11 +374,11 @@ class FolderItem extends React.Component { } handleMouseOver = () => { - this.setState({isIconShown: true}); + this.setState({ isIconShown: true }); }; handleMouseOut = () => { - this.setState({isIconShown: false}); + this.setState({ isIconShown: false }); }; renderFolder = (e) => { diff --git a/frontend/src/components/header.js b/frontend/src/components/header.js index 1cb0109e6f..3d082332b5 100644 --- a/frontend/src/components/header.js +++ b/frontend/src/components/header.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import Logo from './logo'; import CommonToolbar from './toolbar/common-toolbar'; -import './header.css'; +import '../css/header.css'; const propTypes = { children: PropTypes.object, diff --git a/frontend/src/components/main-side-nav-folded.js b/frontend/src/components/main-side-nav-folded.js new file mode 100644 index 0000000000..7cfb659e80 --- /dev/null +++ b/frontend/src/components/main-side-nav-folded.js @@ -0,0 +1,151 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Link } from '@gatsbyjs/reach-router'; +import { gettext, siteRoot, canInvitePeople, enableTC, sideNavFooterCustomHtml, additionalAppBottomLinks, + isDocs, isPro, isDBSqlite3, customNavItems, mediaUrl } from '../utils/constants'; +import Tip from './side-nav-icon-tip'; + +import '../css/main-side-nav-folded.css'; + +const propTypes = { + currentTab: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, + tabItemClick: PropTypes.func.isRequired, + toggleFoldSideNav: PropTypes.func +}; + +class MainSideNavFolded extends React.Component { + + tabItemClick = (e, param, id) => { + if (window.uploader && + window.uploader.isUploadProgressDialogShow && + window.uploader.totalProgress !== 100) { + if (!window.confirm(gettext('A file is being uploaded. Are you sure you want to leave this page?'))) { + e.preventDefault(); + return false; + } + window.uploader.isUploadProgressDialogShow = false; + } + this.props.tabItemClick(param, id); + }; + + getActiveClass = (tab) => { + return this.props.currentTab === tab ? 'active' : ''; + }; + + render() { + let showActivity = isDocs || isPro || !isDBSqlite3; + return ( +
+
+
    + +
  • + this.tabItemClick(e, 'libraries')}> + + +
  • + + +
  • + this.tabItemClick(e, 'starred')}> + + +
  • + + + {showActivity && + <> +
  • + this.tabItemClick(e, 'dashboard')}> + + +
  • + + + } + +
  • + this.tabItemClick(e, 'published')}> + + +
  • + + + {canInvitePeople && + <> +
  • + this.tabItemClick(e, 'invitations')}> + + +
  • + + + } + + {customNavItems && + customNavItems.map((item, idx) => { + return ( +
  • + + + +
  • + ); + }) + } +
+ + {sideNavFooterCustomHtml ? +
+ : +
    +
  • + + + +
  • + + {enableTC && + <> +
  • + + + +
  • + + + } + {additionalAppBottomLinks && ( + <> + {Object.keys(additionalAppBottomLinks).map((key, index) => { + return ( + + + + ); + })} + + )} +
  • + + + +
  • + +
+ } +
+ +
+
+
+ ); + } +} + +MainSideNavFolded.propTypes = propTypes; + +export default MainSideNavFolded; diff --git a/frontend/src/components/main-side-nav.js b/frontend/src/components/main-side-nav.js index 844b69c114..9e25039849 100644 --- a/frontend/src/components/main-side-nav.js +++ b/frontend/src/components/main-side-nav.js @@ -16,7 +16,6 @@ import CreateGroupDialog from '../components/dialog/create-group-dialog'; const propTypes = { currentTab: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, tabItemClick: PropTypes.func.isRequired, - isSidePanelFolded: PropTypes.bool, toggleFoldSideNav: PropTypes.func }; @@ -29,7 +28,6 @@ class MainSideNav extends React.Component { this.state = { filesNavUnfolded: false, sharedExtended: false, - closeSideBar: false, groupItems: [], isCreateGroupDialogOpen: false, }; @@ -217,19 +215,9 @@ class MainSideNav extends React.Component { }); }; - toggleMinimize = () => { - this.setState({ - isMinimized: !this.state.isMinimized - }, () => { - localStorage.setItem('sf_user_side_nav_minimized', this.state.isMinimized); - }); - }; - - render() { let showActivity = isDocs || isPro || !isDBSqlite3; const { filesNavUnfolded } = this.state; - const { isSidePanelFolded } = this.props; return (
@@ -349,12 +337,8 @@ class MainSideNav extends React.Component { }
- {isSidePanelFolded ? : ( - <> - - {gettext('Fold the sidebar')} - - )} + + {gettext('Fold the sidebar')}
diff --git a/frontend/src/components/side-nav-icon-tip.js b/frontend/src/components/side-nav-icon-tip.js new file mode 100644 index 0000000000..bb82d155d9 --- /dev/null +++ b/frontend/src/components/side-nav-icon-tip.js @@ -0,0 +1,43 @@ +import React, { useCallback, useState } from 'react'; +import PropTypes from 'prop-types'; +import { Tooltip } from 'reactstrap'; +import '../css/side-nav-icon-tip.css'; + +function SideNavIconTip(props) { + + const [showTooltip, setShowTooltip] = useState(false); + const [showAnimation, setShowAnimation] = useState(false); + + const toggleTooltip = useCallback(() => { + if (!showTooltip) { + setShowTooltip(true); + setTimeout(() => { + setShowAnimation(true); + }, 10); + } else { + setShowTooltip(false); + setShowAnimation(false); + } + }, [showTooltip]); + + return ( + + {props.text} + + ); +} + +SideNavIconTip.propTypes = { + text: PropTypes.string.isRequired, + target: PropTypes.string.isRequired, +}; + +export default SideNavIconTip; diff --git a/frontend/src/components/side-panel.js b/frontend/src/components/side-panel.js index d1b0a5e596..faec87c119 100644 --- a/frontend/src/components/side-panel.js +++ b/frontend/src/components/side-panel.js @@ -4,6 +4,7 @@ import classnames from 'classnames'; import MediaQuery from 'react-responsive'; import Logo from './logo'; import MainSideNav from './main-side-nav'; +import MainSideNavFolded from './main-side-nav-folded'; import { SIDE_PANEL_FOLDED_WIDTH } from '../constants'; const propTypes = { @@ -32,14 +33,22 @@ class SidePanel extends React.Component {
- {children ? children : ( + {children ? children : null} + {(!children && !isSidePanelFolded) && - )} + } + {(!children && isSidePanelFolded) && + + }
); diff --git a/frontend/src/components/header.css b/frontend/src/css/header.css similarity index 100% rename from frontend/src/components/header.css rename to frontend/src/css/header.css diff --git a/frontend/src/css/main-side-nav-folded.css b/frontend/src/css/main-side-nav-folded.css new file mode 100644 index 0000000000..c54ba2f397 --- /dev/null +++ b/frontend/src/css/main-side-nav-folded.css @@ -0,0 +1,11 @@ +.side-panel .side-nav-folded { + overflow: visible; +} + +.side-panel .side-nav-folded .side-nav-con { + overflow: visible; +} + +.side-panel .side-nav-folded .nav-item .nav-link:hover { + transition: all 250ms cubic-bezier(0.25, 0.1, 0.25, 0.1); +} diff --git a/frontend/src/css/side-nav-icon-tip.css b/frontend/src/css/side-nav-icon-tip.css new file mode 100644 index 0000000000..8fad4fb478 --- /dev/null +++ b/frontend/src/css/side-nav-icon-tip.css @@ -0,0 +1,10 @@ +.side-nav-icon-tip { + opacity: 0; + transform: translateX(0px); + transition: all 250ms cubic-bezier(0.25, 0.1, 0.25, 0.1); +} + +.side-nav-icon-tip.side-nav-icon-tip-animation { + opacity: 1; + transform: translateX(20px); +} diff --git a/frontend/src/utils/repo-trash-api.js b/frontend/src/utils/repo-trash-api.js index 6635fc2022..d29f1fc286 100644 --- a/frontend/src/utils/repo-trash-api.js +++ b/frontend/src/utils/repo-trash-api.js @@ -40,7 +40,7 @@ class RepotrashAPI { page: page || 1, per_page: per_page }; - return this.req.get(url, {params: params}); + return this.req.get(url, { params: params }); } }