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:
@@ -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}
|
||||
/>
|
||||
}
|
||||
|
@@ -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;
|
@@ -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;
|
@@ -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;
|
@@ -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;
|
@@ -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;
|
@@ -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>
|
||||
</>
|
||||
|
@@ -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') : '--'}
|
||||
|
89
frontend/src/components/single-selector.js
Normal file
89
frontend/src/components/single-selector.js
Normal 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;
|
Reference in New Issue
Block a user