1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-20 02:48:51 +00:00

Single selector (#5791)

* [single selector] added a new 'single selector' to replace the 'role editor'

* [sys admin - group - member] replaced the old 'role selector' with the new 'single selector'

* [sys admin - orgs] replaced the old 'role selector' with the new 'single selector'

* [sys admin, org admin] replaced different selectors with the new 'single selector' for users & orgs

* [share link perm selector] replaced it with the new 'single selector'
This commit is contained in:
llj
2023-11-24 18:21:46 +08:00
committed by GitHub
parent 663a78061b
commit 29fa239cd0
21 changed files with 569 additions and 395 deletions

View File

@@ -4,7 +4,7 @@ import { Table } from 'reactstrap';
import { Utils } from '../utils/utils';
import { gettext } from '../utils/constants';
import { seafileAPI } from '../utils/seafile-api';
import RoleEditor from './select-editor/role-editor';
import RoleSelector from './single-selector';
import toaster from './toast';
import OpIcon from './op-icon';
@@ -70,14 +70,17 @@ class Member extends React.PureComponent {
constructor(props) {
super(props);
this.roles = ['Admin', 'Member'];
this.roleOptions = [
{ value: 'Admin', text: gettext('Admin'), isSelected: false },
{ value: 'Member', text: gettext('Member'), isSelected: false }
];
this.state = ({
highlight: false,
});
}
onChangeUserRole = (role) => {
let isAdmin = role === 'Admin' ? 'True' : 'False';
onChangeUserRole = (roleOption) => {
let isAdmin = roleOption.value === 'Admin' ? 'True' : 'False';
seafileAPI.setGroupAdmin(this.props.groupID, this.props.item.email, isAdmin).then((res) => {
this.props.changeMember(res.data);
}).catch(error => {
@@ -124,8 +127,17 @@ class Member extends React.PureComponent {
};
render() {
const { highlight } = this.state;
const { item, isOwner } = this.props;
const deleteAuthority = (item.role !== 'Owner' && isOwner === true) || (item.role === 'Member' && isOwner === false);
const { role: curRole } = item;
this.roleOptions = this.roleOptions.map(item => {
item.isSelected = item.value == curRole;
return item;
});
const currentSelectedOption = this.roleOptions.filter(item => item.isSelected)[0];
return(
<tr onMouseOver={this.handleMouseOver} onMouseLeave={this.handleMouseLeave} className={this.state.highlight ? 'tr-highlight' : ''} tabIndex="0" onFocus={this.handleMouseOver}>
<th scope="row"><img className="avatar" src={item.avatar_url} alt=""/></th>
@@ -135,12 +147,11 @@ class Member extends React.PureComponent {
<span className="group-admin">{this.translateRole(item.role)}</span>
}
{(isOwner === true && item.role !== 'Owner') &&
<RoleEditor
isTextMode={true}
isEditIconShow={this.state.highlight}
currentRole={item.role}
roles={this.roles}
onRoleChanged={this.onChangeUserRole}
<RoleSelector
isDropdownToggleShown={highlight}
currentSelectedOption={currentSelectedOption}
options={this.roleOptions}
selectOption={this.onChangeUserRole}
toggleItemFreezed={this.props.toggleItemFreezed}
/>
}

View File

@@ -1,44 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import SelectEditor from './select-editor';
const propTypes = {
isTextMode: PropTypes.bool.isRequired,
isEditIconShow: PropTypes.bool.isRequired,
roles: PropTypes.array.isRequired,
currentRole: PropTypes.string.isRequired,
onRoleChanged: PropTypes.func.isRequired,
toggleItemFreezed: PropTypes.func,
};
class RoleEditor extends React.Component {
translateRole = (role) => {
if (role === 'Admin') {
return gettext('Admin');
}
if (role === 'Member') {
return gettext('Member');
}
};
render() {
return (
<SelectEditor
isTextMode={this.props.isTextMode}
isEditIconShow={this.props.isEditIconShow}
options={this.props.roles}
currentOption={this.props.currentRole}
onOptionChanged={this.props.onRoleChanged}
translateOption={this.translateRole}
toggleItemFreezed={this.props.toggleItemFreezed}
/>
);
}
}
RoleEditor.propTypes = propTypes;
export default RoleEditor;

View File

@@ -1,36 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Utils } from '../../utils/utils';
import SelectEditor from './select-editor';
const propTypes = {
isTextMode: PropTypes.bool.isRequired,
isEditIconShow: PropTypes.bool.isRequired,
permissionOptions: PropTypes.array.isRequired,
currentPermission: PropTypes.string.isRequired,
onPermissionChanged: PropTypes.func.isRequired
};
class ShareLinkPermissionEditor extends React.Component {
translatePermission = (permission) => {
return Utils.getShareLinkPermissionObject(permission).text;
};
render() {
return (
<SelectEditor
isTextMode={this.props.isTextMode}
isEditIconShow={this.props.isEditIconShow}
options={this.props.permissionOptions}
currentOption={this.props.currentPermission}
onOptionChanged={this.props.onPermissionChanged}
translateOption={this.translatePermission}
/>
);
}
}
ShareLinkPermissionEditor.propTypes = propTypes;
export default ShareLinkPermissionEditor;

View File

@@ -1,43 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import SelectEditor from './select-editor';
const propTypes = {
isTextMode: PropTypes.bool.isRequired,
isEditIconShow: PropTypes.bool.isRequired,
roleOptions: PropTypes.array.isRequired,
currentRole: PropTypes.string.isRequired,
onRoleChanged: PropTypes.func.isRequired
};
class SysAdminGroupRoleEditor extends React.Component {
translateRoles = (role) => {
switch (role) {
case 'Member':
return gettext('Member');
case 'Admin':
return gettext('Admin');
default:
return role;
}
};
render() {
return (
<SelectEditor
isTextMode={this.props.isTextMode}
isEditIconShow={this.props.isEditIconShow}
options={this.props.roleOptions}
currentOption={this.props.currentRole}
onOptionChanged={this.props.onRoleChanged}
translateOption={this.translateRoles}
/>
);
}
}
SysAdminGroupRoleEditor.propTypes = propTypes;
export default SysAdminGroupRoleEditor;

View File

@@ -1,41 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import SelectEditor from './select-editor';
const propTypes = {
isTextMode: PropTypes.bool.isRequired,
isEditIconShow: PropTypes.bool.isRequired,
statusOptions: PropTypes.array.isRequired,
currentStatus: PropTypes.string.isRequired,
onStatusChanged: PropTypes.func.isRequired
};
class SysAdminUserStatusEditor extends React.Component {
translateStatus = (status) => {
switch (status) {
case 'active':
return gettext('Active');
case 'inactive':
return gettext('Inactive');
}
};
render() {
return (
<SelectEditor
isTextMode={this.props.isTextMode}
isEditIconShow={this.props.isEditIconShow}
options={this.props.statusOptions}
currentOption={this.props.currentStatus}
onOptionChanged={this.props.onStatusChanged}
translateOption={this.translateStatus}
/>
);
}
}
SysAdminUserStatusEditor.propTypes = propTypes;
export default SysAdminUserStatusEditor;

View File

@@ -1,43 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import SelectEditor from './select-editor';
const propTypes = {
isTextMode: PropTypes.bool.isRequired,
isEditIconShow: PropTypes.bool.isRequired,
statusArray: PropTypes.array.isRequired,
currentStatus: PropTypes.string.isRequired,
onStatusChanged: PropTypes.func.isRequired
};
class UserStatusEditor extends React.Component {
translateStatus = (userStatus) => {
if (userStatus === 'active') {
return gettext('Active');
}
if (userStatus === 'inactive') {
return gettext('Inactive');
}
};
render() {
return (
<SelectEditor
isTextMode={this.props.isTextMode}
isEditIconShow={this.props.isEditIconShow}
options={this.props.statusArray}
currentOption={this.props.currentStatus}
onOptionChanged={this.props.onStatusChanged}
translateOption={this.translateStatus}
/>
);
}
}
UserStatusEditor.propTypes = propTypes;
export default UserStatusEditor;

View File

@@ -4,7 +4,7 @@ import moment from 'moment';
import copy from 'copy-to-clipboard';
import { Button } from 'reactstrap';
import { isPro, gettext, shareLinkExpireDaysMin, shareLinkExpireDaysMax, shareLinkExpireDaysDefault, canSendShareLinkEmail } from '../../utils/constants';
import ShareLinkPermissionEditor from '../../components/select-editor/share-link-permission-editor';
import Selector from '../../components/single-selector';
import CommonOperationConfirmationDialog from '../../components/dialog/common-operation-confirmation-dialog';
import { seafileAPI } from '../../utils/seafile-api';
import { Utils } from '../../utils/utils';
@@ -119,9 +119,9 @@ class LinkDetails extends React.Component {
this.setState({isOpIconShown: false});
};
changePerm = (permission) => {
changePerm = (permOption) => {
const { sharedLinkInfo } = this.props;
const { permissionDetails } = Utils.getShareLinkPermissionObject(permission);
const { permissionDetails } = Utils.getShareLinkPermissionObject(permOption.value);
seafileAPI.updateShareLink(sharedLinkInfo.token, JSON.stringify(permissionDetails)).then((res) => {
this.props.updateLink(new ShareLink(res.data));
}).catch((error) => {
@@ -152,6 +152,15 @@ class LinkDetails extends React.Component {
const { sharedLinkInfo, permissionOptions } = this.props;
const { isOpIconShown } = this.state;
const currentPermission = Utils.getShareLinkPermissionStr(sharedLinkInfo.permissions);
this.permOptions = permissionOptions.map(item => {
return {
value: item,
text: Utils.getShareLinkPermissionObject(item).text,
isSelected: item == currentPermission
};
});
const currentSelectedPermOption = this.permOptions.filter(item => item.isSelected)[0];
return (
<div>
<button className="fa fa-arrow-left back-icon border-0 bg-transparent text-secondary p-0" onClick={this.goBack} title={gettext('Back')} aria-label={gettext('Back')}></button>
@@ -231,12 +240,11 @@ class LinkDetails extends React.Component {
<>
<dt className="text-secondary font-weight-normal">{gettext('Permission:')}</dt>
<dd style={{width:'250px'}} onMouseEnter={this.handleMouseOver} onMouseLeave={this.handleMouseOut}>
<ShareLinkPermissionEditor
isTextMode={true}
isEditIconShow={isOpIconShown && !sharedLinkInfo.is_expired}
currentPermission={currentPermission}
permissionOptions={permissionOptions}
onPermissionChanged={this.changePerm}
<Selector
isDropdownToggleShown={isOpIconShown && !sharedLinkInfo.is_expired}
currentSelectedOption={currentSelectedPermOption}
options={this.permOptions}
selectOption={this.changePerm}
/>
</dd>
</>

View File

@@ -4,7 +4,6 @@ import moment from 'moment';
import copy from 'copy-to-clipboard';
import toaster from '../toast';
import { isPro, gettext } from '../../utils/constants';
import ShareLinkPermissionEditor from '../../components/select-editor/share-link-permission-editor';
import { Utils } from '../../utils/utils';
import CommonOperationConfirmationDialog from '../../components/dialog/common-operation-confirmation-dialog';
@@ -105,15 +104,7 @@ class LinkItem extends React.Component {
{this.cutLink(link)}
</td>
<td>
{(isPro && permissions) && (
<ShareLinkPermissionEditor
isTextMode={true}
isEditIconShow={false}
currentPermission={currentPermission}
permissionOptions={permissionOptions}
onPermissionChanged={() => {}}
/>
)}
{(isPro && permissions) && Utils.getShareLinkPermissionObject(currentPermission).text}
</td>
<td>
{expire_date ? moment(expire_date).format('YYYY-MM-DD HH:mm') : '--'}

View File

@@ -0,0 +1,89 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import '../css/single-selector.css';
const propTypes = {
isDropdownToggleShown: PropTypes.bool.isRequired,
currentSelectedOption: PropTypes.object.isRequired,
options: PropTypes.array.isRequired,
selectOption: PropTypes.func.isRequired,
toggleItemFreezed: PropTypes.func
};
class Selector extends Component {
constructor(props) {
super(props);
this.state = {
isPopoverOpen: false
};
}
componentDidMount() {
document.addEventListener('click', this.handleOutsideClick);
}
componentWillUnmount() {
document.removeEventListener('click', this.handleOutsideClick);
}
handleOutsideClick = (e) => {
const { isPopoverOpen } = this.state;
if (isPopoverOpen && !this.selector.contains(e.target)) {
this.togglePopover();
}
};
togglePopover = () => {
this.setState({
isPopoverOpen: !this.state.isPopoverOpen
}, () => {
if (this.props.toggleItemFreezed) {
this.props.toggleItemFreezed(this.state.isPopoverOpen);
}
});
};
onToggleClick = (e) => {
e.stopPropagation();
this.togglePopover();
};
selectItem = (e, targetItem) => {
e.stopPropagation();
this.props.selectOption(targetItem);
this.togglePopover();
};
render() {
const { isPopoverOpen } = this.state;
const { currentSelectedOption, options, isDropdownToggleShown } = this.props;
return (
<div className="sf-single-selector position-relative">
<span className="cur-option" onClick={this.onToggleClick}>
{currentSelectedOption.text}
{isDropdownToggleShown && <i className="fas fa-caret-down ml-2 toggle-icon"></i>}
</span>
{isPopoverOpen && (
<div className="options-container position-absolute rounded shadow mt-1" ref={ref => this.selector = ref}>
<ul className="option-list list-unstyled p-3 o-auto">
{options.map((item, index) => {
return (
<li key={index} className="option-item h-6 p-1 rounded d-flex justify-content-between align-items-center" onClick={(e) => {this.selectItem(e, item);}}>
<span className="option-item-text flex-shrink-0 mr-3">{item.text}</span>
<i className={`sf2-icon-tick text-gray font-weight-bold ${item.isSelected ? '' : 'invisible'}`}></i>
</li>
);
})}
</ul>
</div>
)}
</div>
);
}
}
Selector.propTypes = propTypes;
export default Selector;