diff --git a/frontend/src/components/back-icon.js b/frontend/src/components/back-icon.js new file mode 100644 index 0000000000..a3aa73f7c1 --- /dev/null +++ b/frontend/src/components/back-icon.js @@ -0,0 +1,22 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { gettext } from '../utils/constants'; + +function BackIcon({ onClick }) { + return ( + + + ); +} + +BackIcon.propTypes = { + onClick: PropTypes.func +}; + +export default BackIcon; diff --git a/frontend/src/components/dialog/custom-permission/custom-permission-editor.js b/frontend/src/components/dialog/custom-permission/custom-permission-editor.js index ef16f15166..2a1408d5d1 100644 --- a/frontend/src/components/dialog/custom-permission/custom-permission-editor.js +++ b/frontend/src/components/dialog/custom-permission/custom-permission-editor.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { Alert, FormGroup, Input, Label, Tooltip } from 'reactstrap'; import { gettext } from '../../../utils/constants'; import Loading from '../../loading'; -import OpIcon from '../../op-icon'; +import BackIcon from '../../back-icon'; class CustomPermissionEditor extends React.Component { constructor(props) { @@ -102,11 +102,7 @@ class CustomPermissionEditor extends React.Component {
- + {title}
diff --git a/frontend/src/components/dialog/custom-permission/custom-permission-item.js b/frontend/src/components/dialog/custom-permission/custom-permission-item.js index 92e632d123..32847c44c6 100644 --- a/frontend/src/components/dialog/custom-permission/custom-permission-item.js +++ b/frontend/src/components/dialog/custom-permission/custom-permission-item.js @@ -1,5 +1,6 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; +import classnames from 'classnames'; import { gettext } from '../../../utils/constants'; import OpIcon from '../../op-icon'; @@ -14,20 +15,30 @@ class CustomPermissionItem extends React.Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isShowOperations: false }; } onMouseEnter = () => { - this.setState({ isShowOperations: true }); + this.setState({ + isHighlighted: true, + isShowOperations: true + }); }; onMouseOver = () => { - this.setState({ isShowOperations: true }); + this.setState({ + isHighlighted: true, + isShowOperations: true + }); }; onMouseLeave = () => { - this.setState({ isShowOperations: false }); + this.setState({ + isHighlighted: false, + isShowOperations: false + }); }; onEditCustomPermission = () => { @@ -41,22 +52,32 @@ class CustomPermissionItem extends React.Component { }; render() { + const { isHighlighted } = this.state; const { permission } = this.props; - const { id, name, description } = permission; + const { name, description } = permission; return ( - + {name} {description} {this.state.isShowOperations && ( diff --git a/frontend/src/components/dialog/department-detail-widget/department-group-member-selected.js b/frontend/src/components/dialog/department-detail-widget/department-group-member-selected.js index 9cd00ba743..b439b8616b 100644 --- a/frontend/src/components/dialog/department-detail-widget/department-group-member-selected.js +++ b/frontend/src/components/dialog/department-detail-widget/department-group-member-selected.js @@ -40,7 +40,7 @@ class Item extends Component { {member.name} diff --git a/frontend/src/components/dialog/lib-sub-folder-set-group-permission-dialog.js b/frontend/src/components/dialog/lib-sub-folder-set-group-permission-dialog.js index 02f40932b6..dd3e945e3c 100644 --- a/frontend/src/components/dialog/lib-sub-folder-set-group-permission-dialog.js +++ b/frontend/src/components/dialog/lib-sub-folder-set-group-permission-dialog.js @@ -1,5 +1,6 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; +import classnames from 'classnames'; import { Button, Input, InputGroup } from 'reactstrap'; import { gettext, isPro, siteRoot } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; @@ -8,22 +9,30 @@ import SharePermissionEditor from '../select-editor/share-permission-editor'; import FileChooser from '../file-chooser'; import GroupSelect from '../common/group-select'; import toaster from '../../components/toast'; +import BackIcon from '../../components/back-icon'; class GroupItem extends React.Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isOperationShow: false }; } onMouseEnter = () => { - this.setState({ isOperationShow: true }); + this.setState({ + isHighlighted: true, + isOperationShow: true + }); }; onMouseLeave = () => { - this.setState({ isOperationShow: false }); + this.setState({ + isHighlighted: false, + isOperationShow: false + }); }; deleteGroupPermissionItem = () => { @@ -38,8 +47,16 @@ class GroupItem extends React.Component { render() { let item = this.props.item; + const { isHighlighted } = this.state; return ( - + {item.group_name} @@ -59,16 +76,16 @@ class GroupItem extends React.Component { /> - - + ); @@ -282,7 +299,7 @@ class LibSubFolderSetGroupPermissionDialog extends React.Component { <>
- + {gettext('Add Folder')}
diff --git a/frontend/src/components/dialog/lib-sub-folder-set-user-permission-dialog.js b/frontend/src/components/dialog/lib-sub-folder-set-user-permission-dialog.js index fb52e69fd0..2ca2f434c4 100644 --- a/frontend/src/components/dialog/lib-sub-folder-set-user-permission-dialog.js +++ b/frontend/src/components/dialog/lib-sub-folder-set-user-permission-dialog.js @@ -1,5 +1,6 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; +import classnames from 'classnames'; import { gettext, isPro, siteRoot } from '../../utils/constants'; import { Button, Input, InputGroup } from 'reactstrap'; import { seafileAPI } from '../../utils/seafile-api'; @@ -8,22 +9,30 @@ import UserSelect from '../user-select'; import SharePermissionEditor from '../select-editor/share-permission-editor'; import FileChooser from '../file-chooser'; import toaster from '../../components/toast'; +import BackIcon from '../../components/back-icon'; class UserItem extends React.Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isOperationShow: false }; } onMouseEnter = () => { - this.setState({ isOperationShow: true }); + this.setState({ + isHighlighted: true, + isOperationShow: true + }); }; onMouseLeave = () => { - this.setState({ isOperationShow: false }); + this.setState({ + isHighlighted: false, + isOperationShow: false + }); }; deleteUserFolderPermission = () => { @@ -39,8 +48,16 @@ class UserItem extends React.Component { render() { let item = this.props.item; let currentPermission = item.permission; + const { isHighlighted } = this.state; return ( - + {item.user_name} @@ -60,16 +77,16 @@ class UserItem extends React.Component { /> - - + ); @@ -243,7 +260,7 @@ class LibSubFolderSetUserPermissionDialog extends React.Component { <>
- + {gettext('Add Folder')}
diff --git a/frontend/src/components/dialog/my-deleted-repos-dialog/repo-item.js b/frontend/src/components/dialog/my-deleted-repos-dialog/repo-item.js index ee635bc579..3e5eac701f 100644 --- a/frontend/src/components/dialog/my-deleted-repos-dialog/repo-item.js +++ b/frontend/src/components/dialog/my-deleted-repos-dialog/repo-item.js @@ -29,8 +29,7 @@ const RepoItem = ({ repo, filterRestoredRepo }) => { setHighlight(false); }, []); - const restoreDeletedRepo = useCallback((event) => { - event.preventDefault(); + const restoreDeletedRepo = useCallback(() => { seafileAPI.restoreDeletedRepo(repoID).then(res => { const message = gettext('Successfully restored the library {library_name}.').replace('{library_name}', repoName); toaster.success(message); @@ -53,10 +52,14 @@ const RepoItem = ({ repo, filterRestoredRepo }) => { {repoName} {localTime} - - + + ); diff --git a/frontend/src/components/dialog/op-menu.js b/frontend/src/components/dialog/op-menu.js index ab4f9ca850..b59bd22e0e 100644 --- a/frontend/src/components/dialog/op-menu.js +++ b/frontend/src/components/dialog/op-menu.js @@ -49,7 +49,7 @@ class OpMenu extends React.Component { { - this.setState({ isOperationShow: true }); + this.setState({ + isHighlighted: true, + isOperationShow: true + }); }; onMouseLeave = () => { - this.setState({ isOperationShow: false }); + this.setState({ + isHighlighted: false, + isOperationShow: false + }); }; onDeleteAPIToken = () => { @@ -50,9 +58,18 @@ class APITokenItem extends React.Component { render() { let item = this.props.item; + const { isHighlighted } = this.state; return ( - + {item.app_name} - {item.api_token} - {this.state.isOperationShow && +
+ {item.api_token} + {this.state.isOperationShow && - } + } +
diff --git a/frontend/src/components/dialog/repo-history.js b/frontend/src/components/dialog/repo-history.js index bbca997420..63894c8dc5 100644 --- a/frontend/src/components/dialog/repo-history.js +++ b/frontend/src/components/dialog/repo-history.js @@ -214,8 +214,7 @@ class Item extends React.Component { }); }; - editLabel = (e) => { - e.preventDefault(); + editLabel = () => { this.setState({ isCommitLabelUpdateDialogOpen: !this.state.isCommitLabelUpdateDialogOpen }); @@ -268,7 +267,14 @@ class Item extends React.Component { return {item}; })} {userPerm == 'rw' && - + + } } diff --git a/frontend/src/components/dialog/repo-share-admin/group-shares.js b/frontend/src/components/dialog/repo-share-admin/group-shares.js index 82e6816e02..f566ddcb0c 100644 --- a/frontend/src/components/dialog/repo-share-admin/group-shares.js +++ b/frontend/src/components/dialog/repo-share-admin/group-shares.js @@ -1,5 +1,6 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; +import classnames from 'classnames'; import { Link } from '@gatsbyjs/reach-router'; import { Utils } from '../../../utils/utils'; import { seafileAPI } from '../../../utils/seafile-api'; @@ -23,8 +24,8 @@ class Item extends Component { super(props); this.state = { permission: this.props.item.permission, + isHighlighted: false, isOperationShow: false, - isShowPermEditor: false, }; this.permissions = ['rw', 'r']; if (isPro) { @@ -36,11 +37,17 @@ class Item extends Component { } onMouseEnter = () => { - this.setState({ isOperationShow: true }); + this.setState({ + isHighlighted: true, + isOperationShow: true + }); }; onMouseLeave = () => { - this.setState({ isOperationShow: false }); + this.setState({ + isHighlighted: false, + isOperationShow: false + }); }; onDeleteLink = (e) => { @@ -60,12 +67,8 @@ class Item extends Component { }); }; - onEditPermission = (event) => { - event.nativeEvent.stopImmediatePropagation(); - this.setState({ isShowPermEditor: true }); - }; - render() { + const { isHighlighted } = this.state; let objUrl; let item = this.props.item; @@ -74,49 +77,40 @@ class Item extends Component { objUrl = `${siteRoot}library/${item.repo_id}/${encodeURIComponent(item.repo_name)}${Utils.encodePath(path)}`; return ( - + {Utils.getFolderName(item.path)} {item.share_to_name} - {!this.state.isShowPermEditor && ( -
- {item.permission_name || Utils.sharePerms(this.state.permission)} - {this.state.isOperationShow && ( - - - )} -
- )} - {this.state.isShowPermEditor && ( - - )} + - - + ); diff --git a/frontend/src/components/dialog/repo-share-admin/share-links.js b/frontend/src/components/dialog/repo-share-admin/share-links.js index 94ca41a389..26c67b434f 100644 --- a/frontend/src/components/dialog/repo-share-admin/share-links.js +++ b/frontend/src/components/dialog/repo-share-admin/share-links.js @@ -23,16 +23,23 @@ class Item extends Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isOperationShow: false }; } onMouseEnter = () => { - this.setState({ isOperationShow: true }); + this.setState({ + isHighlighted: true, + isOperationShow: true + }); }; onMouseLeave = () => { - this.setState({ isOperationShow: false }); + this.setState({ + isHighlighted: false, + isOperationShow: false + }); }; onDeleteLink = () => { @@ -50,6 +57,7 @@ class Item extends Component { }; render() { + const { isHighlighted } = this.state; let objUrl; let item = this.props.item; let path = item.path === '/' ? '/' : item.path.slice(0, item.path.length - 1); @@ -65,7 +73,10 @@ class Item extends Component { onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onFocus={this.onMouseEnter} - className={classnames({ 'tr-highlight': item.isSelected })} + className={classnames({ + 'tr-highlight': isHighlighted, + 'tr-active': item.isSelected + })} > } {gettext('Creator')} - {gettext('Name')} - {gettext('Link')} - {gettext('Expiration')} + {gettext('Name')} + {gettext('Link')} + {gettext('Expiration')} {gettext('Visits')} - + ); }; diff --git a/frontend/src/components/dialog/repo-share-admin/upload-links.js b/frontend/src/components/dialog/repo-share-admin/upload-links.js index d70ee5fb3f..c6b2463c96 100644 --- a/frontend/src/components/dialog/repo-share-admin/upload-links.js +++ b/frontend/src/components/dialog/repo-share-admin/upload-links.js @@ -23,16 +23,23 @@ class Item extends Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isOperationShow: false }; } onMouseEnter = () => { - this.setState({ isOperationShow: true }); + this.setState({ + isHighlighted: true, + isOperationShow: true + }); }; onMouseLeave = () => { - this.setState({ isOperationShow: false }); + this.setState({ + isHighlighted: false, + isOperationShow: false + }); }; onDeleteLink = () => { @@ -50,6 +57,7 @@ class Item extends Component { }; render() { + const { isHighlighted } = this.state; let item = this.props.item; let path = item.path === '/' ? '/' : item.path.slice(0, item.path.length - 1); let objUrl = `${siteRoot}library/${item.repo_id}/${encodeURIComponent(item.repo_name)}${Utils.encodePath(path)}`; @@ -59,7 +67,10 @@ class Item extends Component { onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onFocus={this.onMouseEnter} - className={classnames({ 'tr-highlight': item.isSelected })} + className={classnames({ + 'tr-highlight': isHighlighted, + 'tr-active': item.isSelected + })} > } {gettext('Creator')} - {gettext('Name')} - {gettext('Link')} - {gettext('Expiration')} + {gettext('Name')} + {gettext('Link')} + {gettext('Expiration')} {gettext('Visits')} - + ); }; diff --git a/frontend/src/components/dialog/repo-share-admin/user-shares.js b/frontend/src/components/dialog/repo-share-admin/user-shares.js index 36fd4723a3..bac936d51a 100644 --- a/frontend/src/components/dialog/repo-share-admin/user-shares.js +++ b/frontend/src/components/dialog/repo-share-admin/user-shares.js @@ -1,5 +1,6 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; +import classnames from 'classnames'; import { Link } from '@gatsbyjs/reach-router'; import { Utils } from '../../../utils/utils'; import { seafileAPI } from '../../../utils/seafile-api'; @@ -21,8 +22,8 @@ class Item extends Component { super(props); this.state = { permission: this.props.item.permission, + isHighlighted: false, isOperationShow: false, - isShowPermEditor: false, }; this.permissions = ['rw', 'r']; if (isPro) { @@ -34,11 +35,17 @@ class Item extends Component { } onMouseEnter = () => { - this.setState({ isOperationShow: true }); + this.setState({ + isHighlighted: true, + isOperationShow: true + }); }; onMouseLeave = () => { - this.setState({ isOperationShow: false }); + this.setState({ + isHighlighted: false, + isOperationShow: false + }); }; onDeleteLink = (e) => { @@ -58,12 +65,8 @@ class Item extends Component { }); }; - onEditPermission = (event) => { - event.nativeEvent.stopImmediatePropagation(); - this.setState({ isShowPermEditor: true }); - }; - render() { + const { isHighlighted } = this.state; let objUrl; let item = this.props.item; @@ -72,49 +75,40 @@ class Item extends Component { objUrl = `${siteRoot}library/${item.repo_id}/${encodeURIComponent(item.repo_name)}${Utils.encodePath(path)}`; return ( - + {Utils.getFolderName(item.path)} {item.share_to_name} - {!this.state.isShowPermEditor && ( -
- {item.permission_name || Utils.sharePerms(this.state.permission)} - {this.state.isOperationShow && ( - - - )} -
- )} - {this.state.isShowPermEditor && ( - - )} + - - + ); diff --git a/frontend/src/components/dialog/share-to-group.js b/frontend/src/components/dialog/share-to-group.js index b5e969953f..0f3a797060 100644 --- a/frontend/src/components/dialog/share-to-group.js +++ b/frontend/src/components/dialog/share-to-group.js @@ -1,6 +1,7 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { Button } from 'reactstrap'; +import classnames from 'classnames'; import { gettext, isPro, enableShareToDepartment } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils, isMobile } from '../../utils/utils'; @@ -15,16 +16,23 @@ class GroupItem extends React.Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isOperationShow: false }; } onMouseEnter = () => { - this.setState({ isOperationShow: true }); + this.setState({ + isHighlighted: true, + isOperationShow: true + }); }; onMouseLeave = () => { - this.setState({ isOperationShow: false }); + this.setState({ + isHighlighted: false, + isOperationShow: false + }); }; deleteShareItem = () => { @@ -38,6 +46,7 @@ class GroupItem extends React.Component { }; render() { + const { isHighlighted } = this.state; let item = this.props.item; let currentPermission = Utils.getSharedPermission(item); if (isMobile) { @@ -49,7 +58,7 @@ class GroupItem extends React.Component { repoID={this.props.repoID} isTextMode={true} autoFocus={true} - isEditIconShow={this.state.isOperationShow} + isEditIconShow={true} currentPermission={currentPermission} permissions={this.props.permissions} onPermissionChanged={this.onChangeUserPermission} @@ -59,7 +68,7 @@ class GroupItem extends React.Component { + {item.group_info.name} { - this.setState({ isOperationShow: true }); + this.setState({ + isHighlighted: true, + isOperationShow: true + }); }; onMouseLeave = () => { - this.setState({ isOperationShow: false }); + this.setState({ + isHighlighted: false, + isOperationShow: false + }); }; deleteShareItem = () => { @@ -38,10 +46,19 @@ class UserItem extends React.Component { }; render() { - let item = this.props.item; + const { isHighlighted } = this.state; + const { item } = this.props; let currentPermission = item.is_admin ? 'admin' : item.permission; return ( - + {item.accepter} {dayjs(item.expire_time).format('YYYY-MM-DD')} {item.inviter_name} - - + ); diff --git a/frontend/src/components/dialog/share-to-other-server.js b/frontend/src/components/dialog/share-to-other-server.js index c45e6ace08..f2d1b80178 100644 --- a/frontend/src/components/dialog/share-to-other-server.js +++ b/frontend/src/components/dialog/share-to-other-server.js @@ -1,5 +1,6 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; +import classnames from 'classnames'; import { gettext, ocmRemoteServers } from '../../utils/constants'; import { Input, Button } from 'reactstrap'; import { seafileAPI } from '../../utils/seafile-api'; @@ -20,11 +21,17 @@ class ShareItem extends React.Component { } onMouseEnter = () => { - this.setState({ isOperationShow: true }); + this.setState({ + isHighlighted: true, + isOperationShow: true + }); }; onMouseLeave = () => { - this.setState({ isOperationShow: false }); + this.setState({ + isHighlighted: false, + isOperationShow: false + }); }; deleteShareItem = () => { @@ -40,9 +47,16 @@ class ShareItem extends React.Component { render() { let item = this.props.item; - const { isOperationShow, isOpFrozen } = this.state; + const { isOperationShow, isOpFrozen, isHighlighted } = this.state; return ( - + {item.to_server_name} {item.to_user} {Utils.sharePerms(item.permission)} @@ -57,7 +71,7 @@ class ShareItem extends React.Component { */} diff --git a/frontend/src/components/dialog/share-to-user.js b/frontend/src/components/dialog/share-to-user.js index a98e031102..70028ff02d 100644 --- a/frontend/src/components/dialog/share-to-user.js +++ b/frontend/src/components/dialog/share-to-user.js @@ -18,17 +18,24 @@ class UserItem extends React.Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isOperationShow: false, isUserDetailsPopoverOpen: false }; } onMouseEnter = () => { - this.setState({ isOperationShow: true }); + this.setState({ + isHighlighted: true, + isOperationShow: true + }); }; onMouseLeave = () => { - this.setState({ isOperationShow: false }); + this.setState({ + isHighlighted: false, + isOperationShow: false + }); }; userAvatarOnMouseEnter = () => { @@ -50,7 +57,7 @@ class UserItem extends React.Component { render() { let item = this.props.item; let currentPermission = Utils.getSharedPermission(item); - const { isUserDetailsPopoverOpen } = this.state; + const { isUserDetailsPopoverOpen, isHighlighted } = this.state; if (isMobile) { return ( @@ -99,7 +106,7 @@ class UserItem extends React.Component { +
{!isDesktop && - - + } diff --git a/frontend/src/components/dirent-list-view/dirent-list-item.js b/frontend/src/components/dirent-list-view/dirent-list-item.js index 2440518163..b25f3ed756 100644 --- a/frontend/src/components/dirent-list-view/dirent-list-item.js +++ b/frontend/src/components/dirent-list-view/dirent-list-item.js @@ -697,7 +697,7 @@ class DirentListItem extends React.Component { {selectedDirentList.length > 1 ? <> {this.state.isOperationShow && !dirent.isSelected && -
+
{(dirent.permission === 'rw' || dirent.permission === 'r' || (isCustomPermission && canDownload)) && ( )} : <> {this.state.isOperationShow && -
+
{(dirent.permission === 'rw' || dirent.permission === 'r' || (isCustomPermission && canDownload)) && ( )} @@ -741,6 +742,7 @@ class DirentListItem extends React.Component { )} { + return ( + + + ); +}; + +EditIcon.propTypes = { + onClick: PropTypes.func.isRequired, +}; + +export default EditIcon; diff --git a/frontend/src/components/file-chooser/searcher/index.css b/frontend/src/components/file-chooser/searcher/index.css index a64ce478e8..95672b0259 100644 --- a/frontend/src/components/file-chooser/searcher/index.css +++ b/frontend/src/components/file-chooser/searcher/index.css @@ -16,16 +16,15 @@ .file-chooser-searcher .search-input-container .search-input { width: 100%; - height: 1.875em; + height: 1.75rem; padding-left: 2rem; padding-right: 1.5rem; } .file-chooser-searcher .search-input-container .search-control { position: absolute; - top: 6px; - right: 8px; - font-size: 16px; + top: 2px; + right: 2px; } .file-chooser-search-results-popover .popover { diff --git a/frontend/src/components/file-chooser/searcher/index.js b/frontend/src/components/file-chooser/searcher/index.js index 9154ef3846..693071c5b3 100644 --- a/frontend/src/components/file-chooser/searcher/index.js +++ b/frontend/src/components/file-chooser/searcher/index.js @@ -122,7 +122,7 @@ const Searcher = ({ onUpdateMode, onUpdateSearchStatus, onUpdateSearchResults, o autoFocus /> {inputValue.length !== 0 && ( - + )}
diff --git a/frontend/src/components/group-members.js b/frontend/src/components/group-members.js index 2bda3f81ac..6a929a4370 100644 --- a/frontend/src/components/group-members.js +++ b/frontend/src/components/group-members.js @@ -139,7 +139,13 @@ class Member extends React.PureComponent { const currentSelectedOption = this.roleOptions.filter(item => item.isSelected)[0]; return ( - + {item.name} @@ -159,7 +165,7 @@ class Member extends React.PureComponent { {(deleteAuthority && this.state.highlight) && diff --git a/frontend/src/components/select-editor/repo-api-token-permission-editor.js b/frontend/src/components/select-editor/repo-api-token-permission-editor.js index 7d8426ed1b..eb6d840c19 100644 --- a/frontend/src/components/select-editor/repo-api-token-permission-editor.js +++ b/frontend/src/components/select-editor/repo-api-token-permission-editor.js @@ -58,12 +58,12 @@ class RepoAPITokenPermissionEditor extends React.Component { return (
{(isTextMode && !this.state.isEditing) ? - <> +
{optionTranslation} {this.props.isEditIconShow && - + } - +
: {(!isTextMode || this.state.isEditing) && @@ -127,17 +130,15 @@ class SelectEditor extends React.Component { /> } {(isTextMode && !this.state.isEditing) && -
- {this.props.translateOption(currentOption)} - {this.props.isEditIconShow && ( - - - )} +
+ {this.props.translateOption(currentOption)} + +
}
diff --git a/frontend/src/components/share-link-panel/link-authenticated-emails.js b/frontend/src/components/share-link-panel/link-authenticated-emails.js index f030630486..4c11fa320e 100644 --- a/frontend/src/components/share-link-panel/link-authenticated-emails.js +++ b/frontend/src/components/share-link-panel/link-authenticated-emails.js @@ -3,9 +3,9 @@ import PropTypes from 'prop-types'; import { gettext } from '../../utils/constants'; import { Button } from 'reactstrap'; import { Utils } from '../../utils/utils'; -import Loading from '../loading'; import toaster from '../toast'; import { shareLinkAPI } from '../../utils/share-link-api'; +import BackIcon from '../back-icon'; class EmailItem extends React.Component { @@ -51,7 +51,7 @@ class EmailItem extends React.Component { { const { success, failed } = res.data; let newEmails = []; @@ -177,13 +178,7 @@ class LinkAuthenticatedEmails extends React.Component {
- + {gettext('Authenticated emails')}
@@ -196,7 +191,7 @@ class LinkAuthenticatedEmails extends React.Component { diff --git a/frontend/src/components/share-link-panel/link-authenticated-users.js b/frontend/src/components/share-link-panel/link-authenticated-users.js index add1bc248e..48301d11ce 100644 --- a/frontend/src/components/share-link-panel/link-authenticated-users.js +++ b/frontend/src/components/share-link-panel/link-authenticated-users.js @@ -6,6 +6,7 @@ import { shareLinkAPI } from '../../utils/share-link-api'; import { Utils } from '../../utils/utils'; import UserSelect from '../user-select'; import toaster from '../toast'; +import BackIcon from '../../components/back-icon'; class UserItem extends React.Component { @@ -57,7 +58,7 @@ class UserItem extends React.Component {
- + {gettext('Authenticated users')}
diff --git a/frontend/src/components/share-link-panel/link-creation.js b/frontend/src/components/share-link-panel/link-creation.js index f877630659..24ad36f0b2 100644 --- a/frontend/src/components/share-link-panel/link-creation.js +++ b/frontend/src/components/share-link-panel/link-creation.js @@ -10,6 +10,7 @@ import ShareLink from '../../models/share-link'; import toaster from '../toast'; import SetLinkExpiration from '../set-link-expiration'; import UserSelect from '../user-select'; +import BackIcon from '../../components/back-icon'; const propTypes = { itemPath: PropTypes.string.isRequired, @@ -284,7 +285,7 @@ class LinkCreation extends React.Component {
- + {type === 'batch' ? gettext('Generate links in batch') : gettext('Generate Link')}
diff --git a/frontend/src/components/share-link-panel/link-details.js b/frontend/src/components/share-link-panel/link-details.js index 00cbff4ccd..5afc1b1c2c 100644 --- a/frontend/src/components/share-link-panel/link-details.js +++ b/frontend/src/components/share-link-panel/link-details.js @@ -15,6 +15,7 @@ import SharedLink from '../shared-link'; import SetLinkExpiration from '../set-link-expiration'; import ShareLinkScopeEditor from '../select-editor/share-link-scope-editor'; import SelectEditor from '../select-editor/select-editor'; +import BackIcon from '../../components/back-icon'; const propTypes = { sharedLinkInfo: PropTypes.object.isRequired, @@ -164,7 +165,7 @@ class LinkDetails extends React.Component {
- + {gettext('Link')}
diff --git a/frontend/src/components/share-link-panel/link-item.js b/frontend/src/components/share-link-panel/link-item.js index c7daaa39e5..c9af1a9802 100644 --- a/frontend/src/components/share-link-panel/link-item.js +++ b/frontend/src/components/share-link-panel/link-item.js @@ -2,6 +2,7 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import dayjs from 'dayjs'; import copy from 'copy-to-clipboard'; +import classnames from 'classnames'; import toaster from '../toast'; import { gettext } from '../../utils/constants'; import { Utils } from '../../utils/utils'; @@ -19,6 +20,7 @@ class LinkItem extends React.Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isItemOpVisible: false, isDeleteShareLinkDialogOpen: false }; @@ -26,12 +28,14 @@ class LinkItem extends React.Component { onMouseOver = () => { this.setState({ + isHighlighted: true, isItemOpVisible: true }); }; onMouseOut = () => { this.setState({ + isHighlighted: false, isItemOpVisible: false }); }; @@ -78,7 +82,7 @@ class LinkItem extends React.Component { }; render() { - const { isItemOpVisible } = this.state; + const { isHighlighted, isItemOpVisible } = this.state; const { item } = this.props; const { isSelected = false, permissions, link, expire_date } = item; const currentPermission = Utils.getShareLinkPermissionStr(permissions); @@ -88,7 +92,11 @@ class LinkItem extends React.Component { onClick={this.clickItem} onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut} - className={`cursor-pointer ${isSelected ? 'tr-highlight' : ''}`} + className={classnames('cursor-pointer', { + 'tr-highlight': isHighlighted, + 'tr-active': isSelected + } + )} > - - + + {this.state.isDeleteShareLinkDialogOpen && ( diff --git a/frontend/src/components/shared-repo-list-view/shared-repo-list-item.js b/frontend/src/components/shared-repo-list-view/shared-repo-list-item.js index 9023688da6..8eefab8a84 100644 --- a/frontend/src/components/shared-repo-list-view/shared-repo-list-item.js +++ b/frontend/src/components/shared-repo-list-view/shared-repo-list-item.js @@ -484,7 +484,7 @@ class SharedRepoListItem extends React.Component { if (this.isDepartmentOwnerGroupMember) { const advancedOperations = this.getAdvancedOperations(); return ( - +
{shareOperation} {deleteOperation} @@ -492,7 +492,7 @@ class SharedRepoListItem extends React.Component { tag="i" role="button" tabIndex="0" - className="sf-dropdown-toggle sf3-font-more sf3-font" + className="op-icon sf3-font-more sf3-font" title={gettext('More operations')} aria-label={gettext('More operations')} data-toggle="dropdown" @@ -537,11 +537,11 @@ class SharedRepoListItem extends React.Component { })} - +
); } else { return ( - +
{operations.map(item => { switch (item) { case 'Share': @@ -556,7 +556,7 @@ class SharedRepoListItem extends React.Component { tag="i" role="button" tabIndex="0" - className="sf-dropdown-toggle sf3-font-more sf3-font" + className="op-icon sf3-font-more sf3-font" title={gettext('More operations')} aria-label={gettext('More operations')} data-toggle="dropdown" @@ -578,7 +578,7 @@ class SharedRepoListItem extends React.Component { return null; } })} - +
); } }; @@ -632,7 +632,7 @@ class SharedRepoListItem extends React.Component { title={this.state.isStarred ? gettext('Unstar') : gettext('Star')} aria-label={this.state.isStarred ? gettext('Unstar') : gettext('Star')} onClick={this.onToggleStarRepo} - className={`op-icon m-0 ${this.state.isStarred ? 'sf3-font-star' : 'sf3-font-star-empty'} sf3-font`} + className={`${this.state.isStarred ? 'sf3-font-star' : 'sf3-font-star-empty'} sf3-font`} > diff --git a/frontend/src/components/user-settings/linked-devices.js b/frontend/src/components/user-settings/linked-devices.js index 98d710423f..a38e8482f3 100644 --- a/frontend/src/components/user-settings/linked-devices.js +++ b/frontend/src/components/user-settings/linked-devices.js @@ -1,6 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import dayjs from 'dayjs'; +import classnames from 'classnames'; import relativeTime from 'dayjs/plugin/relativeTime'; import { DropdownItem } from 'reactstrap'; import { seafileAPI } from '../../utils/seafile-api'; @@ -48,7 +49,7 @@ class Content extends Component { const isDesktop = Utils.isDesktop(); return items.length ? ( - +
{isDesktop ? desktopThead : mobileThead} {items.map((item, index) => { @@ -72,6 +73,7 @@ class Item extends Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isOpIconShown: false, unlinked: false, isConfirmUnlinkDialogOpen: false @@ -80,12 +82,14 @@ class Item extends Component { handleMouseOver = () => { this.setState({ + isHighlighted: true, isOpIconShown: true }); }; handleMouseOut = () => { this.setState({ + isHighlighted: false, isOpIconShown: false }); }; @@ -96,9 +100,7 @@ class Item extends Component { }); }; - handleClick = (e) => { - e.preventDefault(); - + handleClick = () => { const data = this.props.data; if (data.is_desktop_client) { this.toggleDialog(); @@ -125,16 +127,31 @@ class Item extends Component { renderDesktop = () => { const data = this.props.data; - let opClasses = 'sf3-font-delete1 sf3-font unlink-device action-icon'; + const { isHighlighted } = this.state; + let opClasses = 'sf3-font-delete1 sf3-font unlink-device op-icon'; opClasses += this.state.isOpIconShown ? '' : ' invisible'; return ( - + ); diff --git a/frontend/src/components/wiki-card-view/wiki-card-item.js b/frontend/src/components/wiki-card-view/wiki-card-item.js index 4526f4d75b..a030d59874 100644 --- a/frontend/src/components/wiki-card-view/wiki-card-item.js +++ b/frontend/src/components/wiki-card-view/wiki-card-item.js @@ -227,7 +227,7 @@ class WikiCardItem extends Component { tag="i" role="button" tabIndex="0" - className="sf-dropdown-toggle sf3-font-more sf3-font" + className="sf3-font-more sf3-font op-icon op-icon-bg-light" title={gettext('More operations')} aria-label={gettext('More operations')} data-toggle="dropdown" diff --git a/frontend/src/css/group-departments.css b/frontend/src/css/group-departments.css index 7dcb4b6be5..28f57921d9 100644 --- a/frontend/src/css/group-departments.css +++ b/frontend/src/css/group-departments.css @@ -53,11 +53,6 @@ border-top: none; } -.department-dialog-content .department-dialog-member-selected .sf3-font-close { - cursor: pointer; - color: #666; -} - .department-dialog-content .department-dialog-group .group-item { cursor: pointer; padding: 5px; diff --git a/frontend/src/css/invitations.css b/frontend/src/css/invitations.css index 34d67ad870..32600a81e3 100644 --- a/frontend/src/css/invitations.css +++ b/frontend/src/css/invitations.css @@ -1,7 +1,7 @@ .invite-accept-icon { color: green; margin-left: 0.5rem; - font-size: 1.25rem; + font-size: 1rem; font-style: normal; line-height: 1; vertical-align: middle; diff --git a/frontend/src/css/shared-dir-view.css b/frontend/src/css/shared-dir-view.css index b64cb870cc..6b1f6cbfb0 100644 --- a/frontend/src/css/shared-dir-view.css +++ b/frontend/src/css/shared-dir-view.css @@ -125,6 +125,10 @@ body { animation: displaySelectedToolbar .3s ease-in-out 1; } +#shared-dir-view #cur-view-change-mode-dropdown { + margin-right: 8px; +} + .grid-item .action-icon { position: absolute; top: 10px; diff --git a/frontend/src/pages/file-history-old/history-item.js b/frontend/src/pages/file-history-old/history-item.js index acc92d1923..51f762c647 100644 --- a/frontend/src/pages/file-history-old/history-item.js +++ b/frontend/src/pages/file-history-old/history-item.js @@ -52,7 +52,11 @@ class HistoryItem extends React.Component { const snapshotURL = `${siteRoot}repo/${historyRepoID}/snapshot/?commit_id=${item.commit_id}`; return ( - + + @@ -84,7 +100,7 @@ class Item extends React.Component { item.accept_time ? : @@ -342,7 +342,7 @@ class MylibRepoListItem extends React.Component { + @@ -215,34 +220,26 @@ class Item extends Component { {item.user_name} : item.group_name} + - ); } else { diff --git a/frontend/src/pages/share-admin/libraries.js b/frontend/src/pages/share-admin/libraries.js index 0aabb5093c..7af78dc3bc 100644 --- a/frontend/src/pages/share-admin/libraries.js +++ b/frontend/src/pages/share-admin/libraries.js @@ -88,12 +88,11 @@ class Item extends Component { let item = this.props.item; this.state = { share_permission: item.share_permission, - share_permission_name: item.share_permission_name, is_admin: item.is_admin, + isHighlighted: false, isOpIconShown: false, isPermSelectDialogOpen: false, // for mobile unshared: false, - isShowPermEditor: false, }; let permissions = ['rw', 'r']; this.permissions = permissions; @@ -113,11 +112,17 @@ class Item extends Component { }; onMouseEnter = () => { - this.setState({ isOpIconShown: true }); + this.setState({ + isHighlighted: true, + isOpIconShown: true + }); }; onMouseLeave = () => { - this.setState({ isOpIconShown: false }); + this.setState({ + isHighlighted: false, + isOpIconShown: false + }); }; changePerm = (permission) => { @@ -145,9 +150,7 @@ class Item extends Component { }); }; - unshare = (e) => { - e.preventDefault(); - + unshare = () => { const item = this.props.item; const share_type = item.share_type; let options = { @@ -171,17 +174,12 @@ class Item extends Component { }); }; - onEditPermission = (event) => { - event.nativeEvent.stopImmediatePropagation(); - this.setState({ isShowPermEditor: true }); - }; - render() { if (this.state.unshared) { return null; } - let { share_permission, share_permission_name, is_admin, isOpIconShown, isPermSelectDialogOpen, isShowPermEditor } = this.state; + let { share_permission, is_admin, isOpIconShown, isPermSelectDialogOpen, isHighlighted } = this.state; let item = this.props.item; Object.assign(item, { share_permission: share_permission, @@ -214,41 +212,40 @@ class Item extends Component { if (this.props.isDesktop) { return ( - + + - ); } else { diff --git a/frontend/src/pages/share-admin/share-links.js b/frontend/src/pages/share-admin/share-links.js index 401ec4d31f..32c06440f6 100644 --- a/frontend/src/pages/share-admin/share-links.js +++ b/frontend/src/pages/share-admin/share-links.js @@ -184,13 +184,11 @@ class Item extends Component { } }; - viewLink = (e) => { - e.preventDefault(); + viewLink = () => { this.toggleLinkDialog(); }; - removeLink = (e) => { - e.preventDefault(); + removeLink = () => { this.props.onRemoveLink(this.props.item); }; @@ -303,26 +301,26 @@ class Item extends Component { : diff --git a/frontend/src/pages/share-admin/upload-links.js b/frontend/src/pages/share-admin/upload-links.js index e67ae7e364..fc07f85926 100644 --- a/frontend/src/pages/share-admin/upload-links.js +++ b/frontend/src/pages/share-admin/upload-links.js @@ -127,13 +127,11 @@ class Item extends Component { }); }; - viewLink = (e) => { - e.preventDefault(); + viewLink = () => { this.toggleLinkDialog(); }; - removeLink = (e) => { - e.preventDefault(); + removeLink = () => { this.props.onRemoveLink(this.props.item); }; @@ -195,8 +193,26 @@ class Item extends Component { : diff --git a/frontend/src/pages/shared-libs/item.js b/frontend/src/pages/shared-libs/item.js index cd77263197..8153a450e2 100644 --- a/frontend/src/pages/shared-libs/item.js +++ b/frontend/src/pages/shared-libs/item.js @@ -196,7 +196,7 @@ class Item extends Component { title={this.state.isStarred ? gettext('Unstar') : gettext('Star')} aria-label={this.state.isStarred ? gettext('Unstar') : gettext('Star')} onClick={this.onToggleStarRepo} - className={`op-icon m-0 ${this.state.isStarred ? 'sf3-font-star' : 'sf3-font-star-empty'} sf3-font`} + className={`${this.state.isStarred ? 'sf3-font-star' : 'sf3-font-star-empty'} sf3-font`} > @@ -208,27 +208,29 @@ class Item extends Component { @@ -258,7 +260,7 @@ class Item extends Component { {data.monitored && } -
+
{(isPro && data.is_admin) && } @@ -269,7 +271,7 @@ class Item extends Component { tag="i" role="button" tabIndex="0" - className={`sf-dropdown-toggle sf3-font-more sf3-font ${iconVisibility}`} + className={`op-icon sf3-font-more sf3-font ${iconVisibility}`} title={gettext('More operations')} aria-label={gettext('More operations')} data-toggle="dropdown" diff --git a/frontend/src/pages/starred/starred.js b/frontend/src/pages/starred/starred.js index d85795314a..0e7556e885 100644 --- a/frontend/src/pages/starred/starred.js +++ b/frontend/src/pages/starred/starred.js @@ -4,6 +4,7 @@ import { DropdownItem } from 'reactstrap'; import { Link, navigate } from '@gatsbyjs/reach-router'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; +import classnames from 'classnames'; import { seafileAPI } from '../../utils/seafile-api'; import { Utils } from '../../utils/utils'; import { gettext, siteRoot, enableVideoThumbnail, enablePDFThumbnail, thumbnailDefaultSize } from '../../utils/constants'; @@ -135,6 +136,7 @@ class Item extends Component { constructor(props) { super(props); this.state = { + isHighlighted: false, showOpIcon: false, unstarred: false }; @@ -142,12 +144,14 @@ class Item extends Component { handleMouseOver = () => { this.setState({ + isHighlighted: true, showOpIcon: true }); }; handleMouseOut = () => { this.setState({ + isHighlighted: false, showOpIcon: false }); }; @@ -209,10 +213,18 @@ class Item extends Component { }; renderDesktop = () => { + const { isHighlighted } = this.state; const data = this.props.data; const linkUrl = data.dirent_view_url; const desktopItem = ( -
+ + @@ -150,7 +164,12 @@ class Item extends Component { {dayjs(item.last_accessed).fromNow()} {isUnlinkDeviceDialogOpen && diff --git a/frontend/src/pages/sys-admin/institutions/institution-info.js b/frontend/src/pages/sys-admin/institutions/institution-info.js index eea6a2598b..3dacc8f418 100644 --- a/frontend/src/pages/sys-admin/institutions/institution-info.js +++ b/frontend/src/pages/sys-admin/institutions/institution-info.js @@ -1,10 +1,11 @@ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Utils } from '../../../utils/utils'; import { systemAdminAPI } from '../../../utils/system-admin-api'; import { gettext } from '../../../utils/constants'; import toaster from '../../../components/toast'; import Loading from '../../../components/loading'; +import EditIcon from '../../../components/edit-icon'; import SysAdminSetInstitutionQuotaDialog from '../../../components/dialog/sysadmin-dialog/set-quota'; import MainPanelTopbar from '../main-panel-topbar'; import InstitutionNav from './institution-nav'; @@ -22,16 +23,6 @@ class Content extends Component { this.setState({ isSetQuotaDialogOpen: !this.state.isSetQuotaDialogOpen }); }; - showEditIcon = (action) => { - return ( - - - ); - }; - render() { const { loading, errorMsg, institutionInfo } = this.props; if (loading) { @@ -42,20 +33,18 @@ class Content extends Component { const { name, user_count, quota_total, quota_used } = institutionInfo; const { isSetQuotaDialogOpen } = this.state; return ( - + <>
{gettext('Name')}
{name}
-
{gettext('Number of members')}
{user_count}
-
{gettext('Space Used')}
{`${Utils.bytesToSize(quota_used)} / ${quota_total > 0 ? Utils.bytesToSize(quota_total) : '--'}`} - {this.showEditIcon(this.toggleSetQuotaDialog)} +
{isSetQuotaDialogOpen && @@ -64,7 +53,7 @@ class Content extends Component { toggle={this.toggleSetQuotaDialog} /> } -
+ ); } } @@ -127,7 +116,7 @@ class InstitutionInfo extends Component { render() { const { institutionInfo } = this.state; return ( - + <>
@@ -142,7 +131,7 @@ class InstitutionInfo extends Component {
-
+ ); } } diff --git a/frontend/src/pages/sys-admin/institutions/institutions.js b/frontend/src/pages/sys-admin/institutions/institutions.js index c7f1e07a3b..a6099206fb 100644 --- a/frontend/src/pages/sys-admin/institutions/institutions.js +++ b/frontend/src/pages/sys-admin/institutions/institutions.js @@ -4,6 +4,7 @@ import { Link } from '@gatsbyjs/reach-router'; import { Button } from 'reactstrap'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; +import classnames from 'classnames'; import { Utils } from '../../../utils/utils'; import { systemAdminAPI } from '../../../utils/system-admin-api'; import { siteRoot, gettext } from '../../../utils/constants'; @@ -97,17 +98,24 @@ class Item extends Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isOpIconShown: false, isDeleteDialogOpen: false }; } handleMouseEnter = () => { - this.setState({ isOpIconShown: true }); + this.setState({ + isHighlighted: true, + isOpIconShown: true + }); }; handleMouseLeave = () => { - this.setState({ isOpIconShown: false }); + this.setState({ + isHighlighted: false, + isOpIconShown: false + }); }; toggleDeleteDialog = (e) => { @@ -123,18 +131,29 @@ class Item extends Component { render() { const { item } = this.props; - const { isOpIconShown, isDeleteDialogOpen } = this.state; + const { isOpIconShown, isDeleteDialogOpen, isHighlighted } = this.state; const institutionName = '' + Utils.HTMLescape(item.name) + ''; const deleteDialogMsg = gettext('Are you sure you want to delete {placeholder} ?').replace('{placeholder}', institutionName); return ( - + {isDeleteDialogOpen && diff --git a/frontend/src/pages/sys-admin/links/share-links.js b/frontend/src/pages/sys-admin/links/share-links.js index 55a9c55396..12cd00bb60 100644 --- a/frontend/src/pages/sys-admin/links/share-links.js +++ b/frontend/src/pages/sys-admin/links/share-links.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { navigate } from '@gatsbyjs/reach-router'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; +import classnames from 'classnames'; import { systemAdminAPI } from '../../../utils/system-admin-api'; import { gettext } from '../../../utils/constants'; import toaster from '../../../components/toast'; @@ -125,18 +126,21 @@ class Item extends Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isOpIconShown: false, }; } handleMouseOver = () => { this.setState({ + isHighlighted: true, isOpIconShown: true }); }; handleMouseOut = () => { this.setState({ + isHighlighted: false, isOpIconShown: false }); }; @@ -156,11 +160,16 @@ class Item extends Component { }; render() { - let { isOpIconShown } = this.state; + let { isOpIconShown, isHighlighted } = this.state; let { item } = this.props; - let deleteIcon = `action-icon sf3-font-delete1 sf3-font ${isOpIconShown ? '' : 'invisible'}`; return ( - + @@ -168,7 +177,12 @@ class Item extends Component { ); diff --git a/frontend/src/pages/sys-admin/links/upload-links.js b/frontend/src/pages/sys-admin/links/upload-links.js index 5d197cc57d..80c7fe1355 100644 --- a/frontend/src/pages/sys-admin/links/upload-links.js +++ b/frontend/src/pages/sys-admin/links/upload-links.js @@ -2,6 +2,7 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; +import classnames from 'classnames'; import { systemAdminAPI } from '../../../utils/system-admin-api'; import { gettext } from '../../../utils/constants'; import toaster from '../../../components/toast'; @@ -101,18 +102,21 @@ class Item extends Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isOpIconShown: false, }; } handleMouseOver = () => { this.setState({ + isHighlighted: true, isOpIconShown: true }); }; handleMouseOut = () => { this.setState({ + isHighlighted: false, isOpIconShown: false }); }; @@ -132,12 +136,17 @@ class Item extends Component { }; render() { - let { isOpIconShown } = this.state; + let { isOpIconShown, isHighlighted } = this.state; let { item } = this.props; - let deleteIcon = `action-icon sf3-font-delete1 sf3-font ${isOpIconShown ? '' : 'invisible'}`; return ( - + @@ -145,7 +154,12 @@ class Item extends Component { diff --git a/frontend/src/pages/sys-admin/orgs/org-info.js b/frontend/src/pages/sys-admin/orgs/org-info.js index 23005216e5..39fca74e1b 100644 --- a/frontend/src/pages/sys-admin/orgs/org-info.js +++ b/frontend/src/pages/sys-admin/orgs/org-info.js @@ -1,4 +1,4 @@ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Row, Col } from 'reactstrap'; import { Utils } from '../../../utils/utils'; @@ -6,6 +6,7 @@ import { systemAdminAPI } from '../../../utils/system-admin-api'; import { gettext, serviceURL } from '../../../utils/constants'; import toaster from '../../../components/toast'; import Loading from '../../../components/loading'; +import EditIcon from '../../../components/edit-icon'; import SysAdminSetOrgQuotaDialog from '../../../components/dialog/sysadmin-dialog/set-quota'; import SysAdminSetOrgNameDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-set-org-name-dialog'; import SysAdminSetOrgMaxUserNumberDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-set-org-max-user-number-dialog'; @@ -35,16 +36,6 @@ class Content extends Component { this.setState({ isSetMaxUserNumberDialogOpen: !this.state.isSetMaxUserNumberDialogOpen }); }; - showEditIcon = (action) => { - return ( - - - ); - }; - render() { const { loading, errorMsg } = this.props; if (loading) { @@ -55,25 +46,25 @@ class Content extends Component { const { org_name, users_count, max_user_number, groups_count, quota, quota_usage, enable_saml_login, metadata_url, domain } = this.props.orgInfo; const { isSetQuotaDialogOpen, isSetNameDialogOpen, isSetMaxUserNumberDialogOpen } = this.state; return ( - + <>
{gettext('Name')}
{org_name} - {this.showEditIcon(this.toggleSetNameDialog)} +
{gettext('Number of members')}
{users_count}
{max_user_number && - + <>
{gettext('Max number of members')}
{max_user_number} - {this.showEditIcon(this.toggleSetMaxUserNumberDialog)} +
-
+ }
{gettext('Number of groups')}
@@ -82,10 +73,10 @@ class Content extends Component {
{gettext('Space Used')}
{`${Utils.bytesToSize(quota_usage)} / ${quota > 0 ? Utils.bytesToSize(quota) : '--'}`} - {this.showEditIcon(this.toggleSetQuotaDialog)} +
{enable_saml_login && - + <>
{gettext('SAML Config')}
@@ -111,7 +102,7 @@ class Content extends Component {
{domain} - + } {isSetQuotaDialogOpen && @@ -134,7 +125,7 @@ class Content extends Component { toggle={this.toggleSetMaxUserNumberDialog} /> } - + ); } } @@ -224,7 +215,7 @@ class OrgInfo extends Component { render() { const { orgInfo } = this.state; return ( - + <>
@@ -242,7 +233,7 @@ class OrgInfo extends Component {
-
+ ); } } diff --git a/frontend/src/pages/sys-admin/orgs/orgs-content.js b/frontend/src/pages/sys-admin/orgs/orgs-content.js index a9c10b1104..32d19f9d8b 100644 --- a/frontend/src/pages/sys-admin/orgs/orgs-content.js +++ b/frontend/src/pages/sys-admin/orgs/orgs-content.js @@ -262,7 +262,12 @@ class Item extends Component { {`${item.last_activity_time ? dayjs(item.last_activity_time).fromNow() : '--'} `} {isDeleteDialogOpen && diff --git a/frontend/src/pages/sys-admin/repos/trash-repos.js b/frontend/src/pages/sys-admin/repos/trash-repos.js index 2da3c2eebb..a2c3460f7a 100644 --- a/frontend/src/pages/sys-admin/repos/trash-repos.js +++ b/frontend/src/pages/sys-admin/repos/trash-repos.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { Button } from 'reactstrap'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; +import classnames from 'classnames'; import { Utils } from '../../../utils/utils'; import { systemAdminAPI } from '../../../utils/system-admin-api'; import { gettext } from '../../../utils/constants'; @@ -121,7 +122,7 @@ class Item extends Component { constructor(props) { super(props); this.state = { - highlight: false, + isHighlighted: false, isOpIconShown: false, isDeleteRepoDialogOpen: false, isRestoreRepoDialogOpen: false @@ -132,7 +133,7 @@ class Item extends Component { if (!this.props.isItemFreezed) { this.setState({ isOpIconShown: true, - highlight: true + isHighlighted: true }); } }; @@ -141,14 +142,14 @@ class Item extends Component { if (!this.props.isItemFreezed) { this.setState({ isOpIconShown: false, - highlight: false + isHighlighted: false }); } }; onUnfreezedItem = () => { this.setState({ - highlight: false, + isHighlighted: false, isOpIconShow: false }); this.props.onUnfreezedItem(); @@ -223,14 +224,20 @@ class Item extends Component { render() { const { repo } = this.props; - const { isOpIconShown, isDeleteRepoDialogOpen, isRestoreRepoDialogOpen } = this.state; + const { isOpIconShown, isDeleteRepoDialogOpen, isRestoreRepoDialogOpen, isHighlighted } = this.state; const iconUrl = Utils.getLibIconUrl(repo); const iconTitle = Utils.getLibIconTitle(repo); const repoName = '' + Utils.HTMLescape(repo.name) + ''; return ( - + + {(multiInstitution && !isAdmin) && + ) : ( - + ); diff --git a/frontend/src/shared-dir-view.js b/frontend/src/shared-dir-view.js index 91ad646373..69d43012ab 100644 --- a/frontend/src/shared-dir-view.js +++ b/frontend/src/shared-dir-view.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { createRoot } from 'react-dom/client'; import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; import dayjs from 'dayjs'; +import classnames from 'classnames'; import relativeTime from 'dayjs/plugin/relativeTime'; import Account from './components/common/account'; import { useGoFileserver, fileServerRoot, gettext, siteRoot, mediaUrl, logoPath, logoWidth, logoHeight, siteTitle, @@ -1066,6 +1067,7 @@ class Item extends React.Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isIconShown: false, isOpMenuOpen: false }; @@ -1076,11 +1078,17 @@ class Item extends React.Component { }; handleMouseOver = () => { - this.setState({ isIconShown: true }); + this.setState({ + isHighlighted: true, + isIconShown: true + }); }; handleMouseOut = () => { - this.setState({ isIconShown: false }); + this.setState({ + isHighlighted: false, + isIconShown: false + }); }; zipDownloadFolder = (e) => { @@ -1111,10 +1119,17 @@ class Item extends React.Component { render() { const { item, isDesktop, mode } = this.props; - const { isIconShown } = this.state; + const { isIconShown, isHighlighted } = this.state; if (item.is_dir) { return isDesktop ? ( - + {showDownloadIcon && + {showDownloadIcon &&
{data.platform} {data.device_name} {data.last_login_ip} {dayjs(data.last_accessed).fromNow()} - + +
{dayjs(item.ctime).format('YYYY-MM-DD HH:mm:ss')} {this.props.index === 0 && {gettext('(current version)')}} @@ -115,7 +119,7 @@ class MoreMenu extends React.PureComponent { ; } - return ( <>
@@ -57,11 +57,7 @@ export default function UserInfo() {
{gettext('Space Used / Quota')}
{`${Utils.bytesToSize(user.quota_usage)} / ${user.quota_total > 0 ? Utils.bytesToSize(user.quota_total) : '--'}`} - - +
{isShowEditDialog && ( diff --git a/frontend/src/pages/invitations/invitations-view.js b/frontend/src/pages/invitations/invitations-view.js index 7f4a1f0557..9c7131a63b 100644 --- a/frontend/src/pages/invitations/invitations-view.js +++ b/frontend/src/pages/invitations/invitations-view.js @@ -2,6 +2,7 @@ import React, { Component, Fragment } from 'react'; import { DropdownItem } from 'reactstrap'; import PropTypes from 'prop-types'; import dayjs from 'dayjs'; +import classnames from 'classnames'; import { gettext } from '../../utils/constants'; import { Utils } from '../../utils/utils'; import { seafileAPI } from '../../utils/seafile-api'; @@ -20,6 +21,7 @@ class Item extends React.Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isOpIconShown: false, isRevokeDialogOpen: false }; @@ -27,12 +29,14 @@ class Item extends React.Component { onMouseEnter = () => { this.setState({ + isHighlighted: true, isOpIconShown: true }); }; onMouseLeave = () => { this.setState({ + isHighlighted: false, isOpIconShown: false }); }; @@ -67,14 +71,26 @@ class Item extends React.Component { }; render() { - const { isOpIconShown, isRevokeDialogOpen } = this.state; + const { + isHighlighted, + isOpIconShown, + isRevokeDialogOpen + } = this.state; const item = this.props.invitation; return ( {this.props.isDesktop ? -
{item.accepter} {dayjs(item.invite_time).format('YYYY-MM-DD')} {dayjs(item.expire_time).format('YYYY-MM-DD')} {(repo.repo_name && this.state.isOpIconShow) && ( -
+
{(repo.repo_name && this.state.isOpIconShow) && ( -
+
- {children || } + {children} {operations.map((item, index) => { diff --git a/frontend/src/pages/org-admin/departments/member-item.js b/frontend/src/pages/org-admin/departments/member-item.js index e5a38a9744..75ba11cbcd 100644 --- a/frontend/src/pages/org-admin/departments/member-item.js +++ b/frontend/src/pages/org-admin/departments/member-item.js @@ -113,7 +113,7 @@ class DepartmentsV2MembersItem extends React.Component { { - this.setState({ isOpIconShown: true }); + this.setState({ + isHighlighted: true, + isOpIconShown: true + }); }; onMouseLeave = () => { - this.setState({ isOpIconShown: false }); + this.setState({ + isHighlighted: false, + isOpIconShown: false + }); }; - unshare = (e) => { - e.preventDefault(); - + unshare = () => { const item = this.props.item; let options = { 'p': item.path @@ -175,18 +178,13 @@ class Item extends Component { }); }; - onEditPermission = (event) => { - event.nativeEvent.stopImmediatePropagation(); - this.setState({ isShowPermEditor: true }); - }; - render() { if (this.state.unshared) { return null; } const item = this.props.item; - let { share_permission, share_permission_name, isOpIconShown, isPermSelectDialogOpen, isShowPermEditor } = this.state; + let { share_permission, isOpIconShown, isPermSelectDialogOpen, isHighlighted } = this.state; let is_readonly = false; if (share_permission == 'r' || share_permission == 'preview') { @@ -206,7 +204,14 @@ class Item extends Component { if (this.props.isDesktop) { return ( -
{iconTitle} {item.folder_name} {item.repo_name} - {!isShowPermEditor && ( -
- {Utils.sharePerms(share_permission) || share_permission_name} - {isOpIconShown && ( - - - )} -
- )} - {isShowPermEditor && ( - - )} + +
+ +
{iconTitle} {item.repo_name} {item.share_type == 'personal' ? {shareTo} : shareTo} - {!isShowPermEditor && ( -
- {Utils.sharePerms(share_permission) || share_permission_name} - {isOpIconShown && ( - - - )} -
- )} - {isShowPermEditor && ( - - )} + +
+ +
{item.view_cnt} {this.renderExpiration()} - {!item.is_expired && - + {!item.is_expired && + - - } - - + + } + + +
{item.view_cnt} {this.renderExpiration()} - {!item.is_expired && } - +
+ {!item.is_expired && + + + } + + +
- {(isPro && data.is_admin) && - - } - - {enableMonitorRepo && - - - - {data.monitored ? gettext('Unwatch File Changes') : gettext('Watch File Changes')} - - - } +
+ {(isPro && data.is_admin) && + + } + + {enableMonitorRepo && + + + + {data.monitored ? gettext('Unwatch File Changes') : gettext('Watch File Changes')} + + + } +
{data.size} {dayjs(data.last_modified).fromNow()}
{ data.thumbnail_url ? @@ -237,7 +249,7 @@ class Item extends Component { { - this.setState({ isOpIconShown: true }); + this.setState({ + isHighlighted: true, + isOpIconShown: true + }); }; handleMouseOut = () => { - this.setState({ isOpIconShown: false }); + this.setState({ + isHighlighted: false, + isOpIconShown: false + }); }; - handleUnlink = (e) => { - e.preventDefault(); + handleUnlink = () => { if (this.props.item.is_desktop_client) { this.toggleUnlinkDeviceDialog(); } else { @@ -133,7 +140,7 @@ class Item extends Component { render() { const item = this.props.item; - const { unlinked, isUnlinkDeviceDialogOpen, isOpIconShown } = this.state; + const { unlinked, isUnlinkDeviceDialogOpen, isHighlighted, isOpIconShown } = this.state; if (unlinked) { return null; @@ -141,7 +148,14 @@ class Item extends Component { return ( -
{item.user_name} {item.platform}{' / '}{item.client_version} {item.device_name} - + +
{item.name} {dayjs(item.ctime).fromNow()} - + +
{item.obj_name} {item.token} {item.view_cnt} {this.renderExpiration()} - + +
{item.path} {item.token} {item.view_cnt} {this.renderExpiration()} - + +
- + +
{iconTitle} {repo.name} diff --git a/frontend/src/pages/sys-admin/terms-and-conditions/item.js b/frontend/src/pages/sys-admin/terms-and-conditions/item.js index a4861584a7..b2c083b89b 100644 --- a/frontend/src/pages/sys-admin/terms-and-conditions/item.js +++ b/frontend/src/pages/sys-admin/terms-and-conditions/item.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; import { processor } from '@seafile/seafile-editor'; +import classnames from 'classnames'; import { gettext } from '../../../utils/constants'; import { Utils } from '../../../utils/utils'; import getPreviewContent from '../../../utils/markdown-utils'; @@ -20,6 +21,7 @@ class Item extends Component { super(props); this.state = { itemContent: '...', + isHighlighted: false, isOpIconShown: false, isUpdateDialogOpen: false, isDeleteDialogOpen: false, @@ -49,7 +51,7 @@ class Item extends Component { if (!this.props.isItemFreezed) { this.setState({ isOpIconShown: true, - highlight: true + isHighlighted: true }); } }; @@ -58,7 +60,7 @@ class Item extends Component { if (!this.props.isItemFreezed) { this.setState({ isOpIconShown: false, - highlight: false + isHighlighted: false }); } }; @@ -88,7 +90,7 @@ class Item extends Component { onUnfreezedItem = () => { this.setState({ - highlight: false, + isHighlighted: false, isOpIconShow: false }); this.props.onUnfreezedItem(); @@ -122,13 +124,19 @@ class Item extends Component { render() { let { item } = this.props; - let { isDeleteDialogOpen, isUpdateDialogOpen, isTermsPreviewDialogOpen } = this.state; + let { isDeleteDialogOpen, isUpdateDialogOpen, isTermsPreviewDialogOpen, isHighlighted } = this.state; let previewContent = getPreviewContent(item.text); let itemName = '' + Utils.HTMLescape(item.name) + ''; let deleteDialogMsg = gettext('Are you sure you want to delete {placeholder} ?').replace('{placeholder}', itemName); return ( -
{item.name} {item.version_number} diff --git a/frontend/src/pages/sys-admin/users/user-info.js b/frontend/src/pages/sys-admin/users/user-info.js index 5bef503f79..1b2d7fd3ee 100644 --- a/frontend/src/pages/sys-admin/users/user-info.js +++ b/frontend/src/pages/sys-admin/users/user-info.js @@ -1,4 +1,4 @@ -import React, { Component, Fragment } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { FormGroup, Label, Input, Button } from 'reactstrap'; import { Utils } from '../../../utils/utils'; @@ -6,6 +6,7 @@ import { systemAdminAPI } from '../../../utils/system-admin-api'; import { gettext, isPro } from '../../../utils/constants'; import toaster from '../../../components/toast'; import Loading from '../../../components/loading'; +import EditIcon from '../../../components/edit-icon'; import SysAdminSetQuotaDialog from '../../../components/dialog/sysadmin-dialog/set-quota'; import SysAdminSetUploadDownloadRateLimitDialog from '../../../components/dialog/sysadmin-dialog/set-upload-download-rate-limit'; import SysAdminUpdateUserDialog from '../../../components/dialog/sysadmin-dialog/update-user'; @@ -83,16 +84,6 @@ class Content extends Component { this.toggleDialog('', ''); }; - showEditIcon = (action) => { - return ( - - - ); - }; - render() { const { loading, errorMsg } = this.props; const { highlight } = this.state; @@ -108,7 +99,7 @@ class Content extends Component { isSetUserUploadRateLimitDialogOpen, isSetUserDownloadRateLimitDialogOpen } = this.state; return ( - + <>
{gettext('Avatar')}
@@ -119,28 +110,28 @@ class Content extends Component {
{user.email}
{user.org_name && - + <>
{gettext('Organization')}
{user.org_name}
-
+ }
{gettext('Name')}
{user.name || '--'} - {this.showEditIcon(this.toggleSetNameDialog)} +
{gettext('Login ID')}
{user.login_id || '--'} - {this.showEditIcon(this.toggleSetUserLoginIDDialog)} +
{gettext('Contact Email')}
{user.contact_email || '--'} - {this.showEditIcon(this.toggleSetUserContactEmailDialog)} +
{gettext('Role')}
@@ -157,24 +148,24 @@ class Content extends Component {
{gettext('Space Used / Quota')}
{`${Utils.bytesToSize(user.quota_usage)} / ${user.quota_total > 0 ? Utils.bytesToSize(user.quota_total) : '--'}`} - {this.showEditIcon(this.toggleSetQuotaDialog)} +
{isPro && - + <>
{gettext('Upload Rate Limit')}
{user.upload_rate_limit > 0 ? user.upload_rate_limit + ' kB/s' : '--'} - {this.showEditIcon(this.toggleSetUserUploadRateLimitDialog)} +
{gettext('Download Rate Limit')}
{user.download_rate_limit > 0 ? user.download_rate_limit + ' kB/s' : '--'} - {this.showEditIcon(this.toggleSetUserDownloadRateLimitDialog)} +
-
+ } {twoFactorAuthEnabled && - + <>
{gettext('Two-Factor Authentication')}
{user.has_default_device ? @@ -193,7 +184,7 @@ class Content extends Component {
-
+ }
{isSetQuotaDialogOpen && @@ -224,7 +215,7 @@ class Content extends Component { toggleDialog={this.toggleUpdateUserDialog} /> } -
+ ); } } @@ -347,7 +338,7 @@ class User extends Component { }; return ( - + <>
@@ -367,7 +358,7 @@ class User extends Component {
-
+ ); } } diff --git a/frontend/src/pages/sys-admin/users/users-content.js b/frontend/src/pages/sys-admin/users/users-content.js index ac2dc15c5d..669b77325d 100644 --- a/frontend/src/pages/sys-admin/users/users-content.js +++ b/frontend/src/pages/sys-admin/users/users-content.js @@ -522,11 +522,11 @@ class Item extends Component { }
{`${Utils.bytesToSize(item.quota_usage)} / ${item.quota_total > 0 ? Utils.bytesToSize(item.quota_total) : '--'}`} - - + diff --git a/frontend/src/repo-snapshot.js b/frontend/src/repo-snapshot.js index 2b03120ce1..195bf208f9 100644 --- a/frontend/src/repo-snapshot.js +++ b/frontend/src/repo-snapshot.js @@ -1,6 +1,7 @@ import React from 'react'; import { createRoot } from 'react-dom/client'; import PropTypes from 'prop-types'; +import classnames from 'classnames'; import { navigate } from '@gatsbyjs/reach-router'; import { Utils } from './utils/utils'; import { gettext, siteRoot, mediaUrl, logoPath, logoWidth, logoHeight, siteTitle } from './utils/constants'; @@ -249,21 +250,26 @@ class FolderItem extends React.Component { constructor(props) { super(props); this.state = { + isHighlighted: false, isIconShown: false }; } handleMouseOver = () => { - this.setState({ isIconShown: true }); + this.setState({ + isHighlighted: true, + isIconShown: true + }); }; handleMouseOut = () => { - this.setState({ isIconShown: false }); + this.setState({ + isHighlighted: false, + isIconShown: false + }); }; - restoreItem = (e) => { - e.preventDefault(); - + restoreItem = () => { const item = this.props.item; const path = Utils.joinPath(this.props.folderPath, item.name); const request = item.type == 'dir' ? @@ -287,26 +293,56 @@ class FolderItem extends React.Component { render() { const item = this.props.item; - const { isIconShown } = this.state; + const { isIconShown, isHighlighted } = this.state; const { folderPath } = this.props; return item.type == 'dir' ? ( -
{gettext('Folder')} {item.name} - + +
{gettext('File')} {item.name} {Utils.bytesToSize(item.size)} - - +
+ + + +
@@ -1155,7 +1170,14 @@ class Item extends React.Component { const fileURL = `${siteRoot}d/${token}/files/?p=${encodeURIComponent(item.file_path)}`; const thumbnailURL = item.encoded_thumbnail_src ? `${siteRoot}${item.encoded_thumbnail_src}?mtime=${item.last_modified}` : ''; return isDesktop ? ( -
diff --git a/media/css/seahub_react.css b/media/css/seahub_react.css index c3eebe2679..fb36eb22f9 100644 --- a/media/css/seahub_react.css +++ b/media/css/seahub_react.css @@ -564,7 +564,32 @@ a, a:hover { } /** op-icon **/ -.op-icon, +.op-icon { + font-size: 1rem; + line-height: 1; + color: #999; + cursor: pointer; + display: inline-flex; + width: 24px; + height: 24px; + align-items: center; + justify-content: center; + border-radius: 3px; + margin-right: 0.25rem; +} + +.op-icon:focus, +.op-icon:hover { + color: #666; + text-decoration: none; + background: #e5e5e5; +} + +.op-icon-bg-light:focus, +.op-icon-bg-light:hover { + background: #efefef; /* for .op-icon shown in white containers */ +} + .action-icon, .attr-action-icon { margin-left: 0.5rem; @@ -575,16 +600,6 @@ a, a:hover { vertical-align: middle; } -.op-icon { - font-size: 1rem; - color: #999; -} - -.op-icon:focus, -.op-icon:hover { - color: #212529; - text-decoration: none; -} .action-icon, .attr-action-icon {