mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-25 14:50:29 +00:00
change search and select user (#7782)
* 01 basic function * 02 add list and add group users * 03 change style * 04 other components * 05 change className
This commit is contained in:
@@ -16,7 +16,6 @@ class UserItem extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
isOperationShow: false
|
isOperationShow: false
|
||||||
};
|
};
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseEnter = () => {
|
onMouseEnter = () => {
|
||||||
@@ -100,7 +99,7 @@ class LibSubFolderSetUserPermissionDialog extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedUsers: null,
|
selectedUsers: [],
|
||||||
errorMsg: [],
|
errorMsg: [],
|
||||||
permission: 'rw',
|
permission: 'rw',
|
||||||
userFolderPermItems: [],
|
userFolderPermItems: [],
|
||||||
@@ -112,8 +111,6 @@ class LibSubFolderSetUserPermissionDialog extends React.Component {
|
|||||||
} else {
|
} else {
|
||||||
this.permissions = ['r', 'rw', 'cloud-edit', 'preview', 'invisible'];
|
this.permissions = ['r', 'rw', 'cloud-edit', 'preview', 'invisible'];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUserSelectChange = (option) => {
|
handleUserSelectChange = (option) => {
|
||||||
@@ -161,11 +158,10 @@ class LibSubFolderSetUserPermissionDialog extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
errorMsg: errorMsg,
|
errorMsg: errorMsg,
|
||||||
userFolderPermItems: this.state.userFolderPermItems.concat(res.data.success),
|
userFolderPermItems: this.state.userFolderPermItems.concat(res.data.success),
|
||||||
selectedUsers: null,
|
selectedUsers: [],
|
||||||
permission: 'rw',
|
permission: 'rw',
|
||||||
folderPath: '',
|
folderPath: '',
|
||||||
});
|
});
|
||||||
this.userSelect.current.clearSelect();
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
toaster.danger(errMessage);
|
toaster.danger(errMessage);
|
||||||
@@ -283,11 +279,10 @@ class LibSubFolderSetUserPermissionDialog extends React.Component {
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
ref={this.userSelect}
|
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
placeholder={gettext('Search users')}
|
placeholder={gettext('Search users')}
|
||||||
onSelectChange={this.handleUserSelectChange}
|
onSelectChange={this.handleUserSelectChange}
|
||||||
value={this.state.selectedUsers}
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
{showPath &&
|
{showPath &&
|
||||||
|
@@ -18,24 +18,23 @@ class AddOrgAdminDialog extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
errMessage: '',
|
errMessage: '',
|
||||||
};
|
};
|
||||||
this.options = [];
|
this.options = [];
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (option) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedOption: option,
|
selectedUsers: option,
|
||||||
errMessage: ''
|
errMessage: ''
|
||||||
});
|
});
|
||||||
this.options = [];
|
this.options = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
addOrgAdmin = () => {
|
addOrgAdmin = () => {
|
||||||
if (!this.state.selectedOption) return;
|
if (!this.state.selectedUsers || this.state.selectedUsers.length === 0) return;
|
||||||
const userEmail = this.state.selectedOption[0].email;
|
const userEmail = this.state.selectedUsers[0].email;
|
||||||
orgAdminAPI.orgAdminSetOrgAdmin(orgID, userEmail, true).then(res => {
|
orgAdminAPI.orgAdminSetOrgAdmin(orgID, userEmail, true).then(res => {
|
||||||
let userInfo = new OrgUserInfo(res.data);
|
let userInfo = new OrgUserInfo(res.data);
|
||||||
this.props.onAddedOrgAdmin(userInfo);
|
this.props.onAddedOrgAdmin(userInfo);
|
||||||
@@ -52,13 +51,13 @@ class AddOrgAdminDialog extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={true} toggle={this.toggle}>
|
<Modal isOpen={true} toggle={this.toggle}>
|
||||||
<SeahubModalHeader toggle={this.toggle}>{gettext('Add Admins')}</SeahubModalHeader>
|
<SeahubModalHeader toggle={this.toggle}>{gettext('Add Admin')}</SeahubModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
ref={this.userSelect}
|
|
||||||
isMulti={false}
|
isMulti={false}
|
||||||
placeholder={gettext('Select a user as admin')}
|
placeholder={gettext('Select a user as admin')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
{this.state.errMessage && <Alert color="danger" className="mt-2">{this.state.errMessage}</Alert>}
|
{this.state.errMessage && <Alert color="danger" className="mt-2">{this.state.errMessage}</Alert>}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
@@ -21,7 +21,6 @@ class UserItem extends React.Component {
|
|||||||
isOperationShow: false,
|
isOperationShow: false,
|
||||||
isUserDetailsPopoverOpen: false
|
isUserDetailsPopoverOpen: false
|
||||||
};
|
};
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseEnter = () => {
|
onMouseEnter = () => {
|
||||||
@@ -225,7 +224,7 @@ class ShareToUser extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
errorMsg: [],
|
errorMsg: [],
|
||||||
permission: 'rw',
|
permission: 'rw',
|
||||||
sharedItems: [],
|
sharedItems: [],
|
||||||
@@ -253,7 +252,7 @@ class ShareToUser extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (option) => {
|
||||||
this.setState({ selectedOption: option });
|
this.setState({ selectedUsers: option });
|
||||||
this.options = [];
|
this.options = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -287,9 +286,9 @@ class ShareToUser extends React.Component {
|
|||||||
let users = [];
|
let users = [];
|
||||||
let path = this.props.itemPath;
|
let path = this.props.itemPath;
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
if (this.state.selectedOption && this.state.selectedOption.length > 0) {
|
if (this.state.selectedUsers && this.state.selectedUsers.length > 0) {
|
||||||
for (let i = 0; i < this.state.selectedOption.length; i ++) {
|
for (let i = 0; i < this.state.selectedUsers.length; i ++) {
|
||||||
users[i] = this.state.selectedOption[i].email;
|
users[i] = this.state.selectedUsers[i].email;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.props.isGroupOwnedRepo) {
|
if (this.props.isGroupOwnedRepo) {
|
||||||
@@ -312,10 +311,9 @@ class ShareToUser extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
errorMsg: errorMsg,
|
errorMsg: errorMsg,
|
||||||
sharedItems: this.state.sharedItems.concat(items),
|
sharedItems: this.state.sharedItems.concat(items),
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
permission: 'rw',
|
permission: 'rw',
|
||||||
});
|
});
|
||||||
this.userSelect.current.clearSelect();
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
let message = gettext('Library can not be shared to owner.');
|
let message = gettext('Library can not be shared to owner.');
|
||||||
@@ -323,7 +321,7 @@ class ShareToUser extends React.Component {
|
|||||||
errMessage.push(message);
|
errMessage.push(message);
|
||||||
this.setState({
|
this.setState({
|
||||||
errorMsg: errMessage,
|
errorMsg: errMessage,
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -338,10 +336,9 @@ class ShareToUser extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
errorMsg: errorMsg,
|
errorMsg: errorMsg,
|
||||||
sharedItems: this.state.sharedItems.concat(res.data.success),
|
sharedItems: this.state.sharedItems.concat(res.data.success),
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
permission: 'rw',
|
permission: 'rw',
|
||||||
});
|
});
|
||||||
this.userSelect.current.clearSelect();
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
let message = gettext('Library can not be shared to owner.');
|
let message = gettext('Library can not be shared to owner.');
|
||||||
@@ -349,7 +346,7 @@ class ShareToUser extends React.Component {
|
|||||||
errMessage.push(message);
|
errMessage.push(message);
|
||||||
this.setState({
|
this.setState({
|
||||||
errorMsg: errMessage,
|
errorMsg: errMessage,
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -443,10 +440,9 @@ class ShareToUser extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
errorMsg: errorMsg,
|
errorMsg: errorMsg,
|
||||||
sharedItems: this.state.sharedItems.concat(items),
|
sharedItems: this.state.sharedItems.concat(items),
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
permission: 'rw',
|
permission: 'rw',
|
||||||
});
|
});
|
||||||
this.userSelect.current.clearSelect();
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
let message = gettext('Library can not be shared to owner.');
|
let message = gettext('Library can not be shared to owner.');
|
||||||
@@ -454,7 +450,7 @@ class ShareToUser extends React.Component {
|
|||||||
errMessage.push(message);
|
errMessage.push(message);
|
||||||
this.setState({
|
this.setState({
|
||||||
errorMsg: errMessage,
|
errorMsg: errMessage,
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -469,10 +465,9 @@ class ShareToUser extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
errorMsg: errorMsg,
|
errorMsg: errorMsg,
|
||||||
sharedItems: this.state.sharedItems.concat(res.data.success),
|
sharedItems: this.state.sharedItems.concat(res.data.success),
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
permission: 'rw',
|
permission: 'rw',
|
||||||
});
|
});
|
||||||
this.userSelect.current.clearSelect();
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
let message = gettext('Library can not be shared to owner.');
|
let message = gettext('Library can not be shared to owner.');
|
||||||
@@ -480,7 +475,7 @@ class ShareToUser extends React.Component {
|
|||||||
errMessage.push(message);
|
errMessage.push(message);
|
||||||
this.setState({
|
this.setState({
|
||||||
errorMsg: errMessage,
|
errorMsg: errMessage,
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -526,12 +521,11 @@ class ShareToUser extends React.Component {
|
|||||||
<td>
|
<td>
|
||||||
<div className='add-members'>
|
<div className='add-members'>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
ref={this.userSelect}
|
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
className={classnames('reviewer-select', { 'user-select-right-btn': showDeptBtn })}
|
className={classnames('reviewer-select', { 'user-select-right-btn': showDeptBtn })}
|
||||||
placeholder={gettext('Search users...')}
|
placeholder={gettext('Search users...')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
excludeCurrentUser={false}
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
{showDeptBtn &&
|
{showDeptBtn &&
|
||||||
<span
|
<span
|
||||||
|
@@ -20,17 +20,17 @@ export default class AddDepartMemberV2Dialog extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedOptions: [],
|
selectedUsers: [],
|
||||||
errMsgs: '',
|
errMsgs: '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (options) => {
|
handleSelectChange = (options) => {
|
||||||
this.setState({ selectedOptions: options });
|
this.setState({ selectedUsers: options });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSubmit = () => {
|
handleSubmit = () => {
|
||||||
const emails = this.state.selectedOptions.map(option => option.email);
|
const emails = this.state.selectedUsers.map(option => option.email);
|
||||||
if (emails.length === 0) return;
|
if (emails.length === 0) return;
|
||||||
this.setState({ errMessage: '' });
|
this.setState({ errMessage: '' });
|
||||||
const { nodeId, orgID } = this.props;
|
const { nodeId, orgID } = this.props;
|
||||||
@@ -38,7 +38,7 @@ export default class AddDepartMemberV2Dialog extends React.Component {
|
|||||||
orgAdminAPI.orgAdminAddGroupMember(orgID, nodeId, emails) :
|
orgAdminAPI.orgAdminAddGroupMember(orgID, nodeId, emails) :
|
||||||
systemAdminAPI.sysAdminAddGroupMember(nodeId, emails);
|
systemAdminAPI.sysAdminAddGroupMember(nodeId, emails);
|
||||||
req.then((res) => {
|
req.then((res) => {
|
||||||
this.setState({ selectedOptions: [] });
|
this.setState({ selectedUsers: [] });
|
||||||
if (res.data.failed.length > 0) {
|
if (res.data.failed.length > 0) {
|
||||||
this.setState({ errMsgs: res.data.failed.map(item => item.error_msg) });
|
this.setState({ errMsgs: res.data.failed.map(item => item.error_msg) });
|
||||||
}
|
}
|
||||||
@@ -62,6 +62,7 @@ export default class AddDepartMemberV2Dialog extends React.Component {
|
|||||||
placeholder={gettext('Search users')}
|
placeholder={gettext('Search users')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
{errMsgs.length > 0 && (
|
{errMsgs.length > 0 && (
|
||||||
<ul className="list-unstyled">
|
<ul className="list-unstyled">
|
||||||
|
@@ -15,17 +15,17 @@ class AddMemberDialog extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedOption: [],
|
selectedUsers: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (option) => {
|
||||||
this.setState({ selectedOption: option });
|
this.setState({ selectedUsers: option });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSubmit = () => {
|
handleSubmit = () => {
|
||||||
if (!this.state.selectedOption) return;
|
if (!this.state.selectedUsers) return;
|
||||||
const emails = this.state.selectedOption.map(item => item.email);
|
const emails = this.state.selectedUsers.map(item => item.email);
|
||||||
this.props.addUser(emails);
|
this.props.addUser(emails);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,6 +39,7 @@ class AddMemberDialog extends React.Component {
|
|||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
className='org-add-member-select'
|
className='org-add-member-select'
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
@@ -19,22 +19,21 @@ class AddMemberDialog extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
errMessage: '',
|
errMessage: '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (option) => {
|
||||||
this.setState({ selectedOption: option });
|
this.setState({ selectedUsers: option });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSubmit = () => {
|
handleSubmit = () => {
|
||||||
if (!this.state.selectedOption) return;
|
if (!this.state.selectedUsers) return;
|
||||||
const emails = this.state.selectedOption.map(item => item.email);
|
const emails = this.state.selectedUsers.map(item => item.email);
|
||||||
this.refs.orgSelect.clearSelect();
|
|
||||||
this.setState({ errMessage: [] });
|
this.setState({ errMessage: [] });
|
||||||
systemAdminAPI.sysAdminAddGroupMember(this.props.groupID, emails).then((res) => {
|
systemAdminAPI.sysAdminAddGroupMember(this.props.groupID, emails).then((res) => {
|
||||||
this.setState({ selectedOption: null });
|
this.setState({ selectedUsers: [] });
|
||||||
if (res.data.failed.length > 0) {
|
if (res.data.failed.length > 0) {
|
||||||
this.setState({ errMessage: res.data.failed[0].error_msg });
|
this.setState({ errMessage: res.data.failed[0].error_msg });
|
||||||
}
|
}
|
||||||
@@ -56,9 +55,9 @@ class AddMemberDialog extends React.Component {
|
|||||||
<UserSelect
|
<UserSelect
|
||||||
placeholder={gettext('Search users')}
|
placeholder={gettext('Search users')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
ref="orgSelect"
|
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
className='org-add-member-select'
|
className='org-add-member-select'
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
{ this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
{ this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
@@ -14,7 +14,7 @@ class SysAdminBatchAddAdminDialog extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
options: null,
|
selectedUsers: [],
|
||||||
isSubmitBtnActive: false
|
isSubmitBtnActive: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -23,15 +23,15 @@ class SysAdminBatchAddAdminDialog extends React.Component {
|
|||||||
this.props.toggle();
|
this.props.toggle();
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSelectChange = (options) => {
|
handleSelectChange = (selectedUsers) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
options: options,
|
selectedUsers: selectedUsers,
|
||||||
isSubmitBtnActive: options.length > 0
|
isSubmitBtnActive: selectedUsers.length > 0
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSubmit = () => {
|
handleSubmit = () => {
|
||||||
this.props.addAdminInBatch(this.state.options.map(item => item.email));
|
this.props.addAdminInBatch(this.state.selectedUsers.map(item => item.email));
|
||||||
this.toggle();
|
this.toggle();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -44,6 +44,7 @@ class SysAdminBatchAddAdminDialog extends React.Component {
|
|||||||
isMulti={true}
|
isMulti={true}
|
||||||
placeholder={gettext('Search users')}
|
placeholder={gettext('Search users')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
@@ -16,7 +16,7 @@ class SysAdminCreateGroupDialog extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
groupName: '',
|
groupName: '',
|
||||||
ownerEmail: '',
|
selectedUsers: [],
|
||||||
errMessage: '',
|
errMessage: '',
|
||||||
isSubmitBtnActive: false
|
isSubmitBtnActive: false
|
||||||
};
|
};
|
||||||
@@ -34,13 +34,12 @@ class SysAdminCreateGroupDialog extends React.Component {
|
|||||||
|
|
||||||
handleSubmit = () => {
|
handleSubmit = () => {
|
||||||
let groupName = this.state.groupName.trim();
|
let groupName = this.state.groupName.trim();
|
||||||
this.props.createGroup(groupName, this.state.ownerEmail);
|
this.props.createGroup(groupName, this.state.selectedUsers[0].email);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (selectedUsers) => {
|
||||||
// option can be `null`, `[{...}]`, or `[]`
|
|
||||||
this.setState({
|
this.setState({
|
||||||
ownerEmail: option && option.length ? option[0].email : ''
|
selectedUsers: selectedUsers
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,12 +71,13 @@ class SysAdminCreateGroupDialog extends React.Component {
|
|||||||
/>
|
/>
|
||||||
<Label className="mt-2">
|
<Label className="mt-2">
|
||||||
{gettext('Owner')}
|
{gettext('Owner')}
|
||||||
<span className="small text-secondary">{gettext('(If left blank, owner will be admin)')}</span>
|
<span className="small text-secondary ml-1">{gettext('(If left blank, owner will be admin)')}</span>
|
||||||
</Label>
|
</Label>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
isMulti={false}
|
isMulti={false}
|
||||||
placeholder={gettext('Select a user')}
|
placeholder={gettext('Select a user')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Form>
|
</Form>
|
||||||
|
@@ -15,7 +15,7 @@ class SysAdminCreateRepoDialog extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
repoName: '',
|
repoName: '',
|
||||||
ownerEmail: '',
|
selectedUsers: [],
|
||||||
errMessage: '',
|
errMessage: '',
|
||||||
isSubmitBtnActive: false
|
isSubmitBtnActive: false
|
||||||
};
|
};
|
||||||
@@ -30,15 +30,14 @@ class SysAdminCreateRepoDialog extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleSubmit = () => {
|
handleSubmit = () => {
|
||||||
const { repoName, ownerEmail } = this.state;
|
const { repoName, selectedUsers } = this.state;
|
||||||
this.props.createRepo(repoName.trim(), ownerEmail);
|
this.props.createRepo(repoName.trim(), selectedUsers[0].email);
|
||||||
this.toggle();
|
this.toggle();
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (selectedUsers) => {
|
||||||
// option can be `null`, `[{...}]`, or `[]`
|
|
||||||
this.setState({
|
this.setState({
|
||||||
ownerEmail: option && option.length ? option[0].email : ''
|
selectedUsers: selectedUsers
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,13 +71,14 @@ class SysAdminCreateRepoDialog extends React.Component {
|
|||||||
<FormGroup>
|
<FormGroup>
|
||||||
<Label for="userSelect">
|
<Label for="userSelect">
|
||||||
{gettext('Owner')}
|
{gettext('Owner')}
|
||||||
<span className="small text-secondary">{gettext('(If left blank, owner will be admin)')}</span>
|
<span className="small text-secondary ml-1">{gettext('(If left blank, owner will be admin)')}</span>
|
||||||
</Label>
|
</Label>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
id="userSelect"
|
id="userSelect"
|
||||||
isMulti={false}
|
isMulti={false}
|
||||||
placeholder={gettext('Select a user')}
|
placeholder={gettext('Select a user')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Form>
|
</Form>
|
||||||
|
@@ -15,21 +15,20 @@ class SysAdminGroupAddMemberDialog extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedOptions: null,
|
selectedUsers: [],
|
||||||
isSubmitBtnDisabled: true
|
isSubmitBtnDisabled: true
|
||||||
};
|
};
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (options) => {
|
handleSelectChange = (options) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedOptions: options,
|
selectedUsers: options,
|
||||||
isSubmitBtnDisabled: !options.length
|
isSubmitBtnDisabled: !options.length
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
addMembers = () => {
|
addMembers = () => {
|
||||||
let emails = this.state.selectedOptions.map(item => item.email);
|
let emails = this.state.selectedUsers.map(item => item.email);
|
||||||
this.props.addMembers(emails);
|
this.props.addMembers(emails);
|
||||||
this.props.toggle();
|
this.props.toggle();
|
||||||
};
|
};
|
||||||
@@ -41,10 +40,10 @@ class SysAdminGroupAddMemberDialog extends React.Component {
|
|||||||
<SeahubModalHeader toggle={this.props.toggle}>{gettext('Add Member')}</SeahubModalHeader>
|
<SeahubModalHeader toggle={this.props.toggle}>{gettext('Add Member')}</SeahubModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
ref={this.userSelect}
|
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
placeholder={gettext('Search users')}
|
placeholder={gettext('Search users')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
@@ -17,22 +17,21 @@ class SysAdminTransferGroupDialog extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedOptions: null,
|
selectedUsers: [],
|
||||||
submitBtnDisabled: true
|
submitBtnDisabled: true
|
||||||
};
|
};
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (options) => {
|
handleSelectChange = (options) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedOptions: options,
|
selectedUsers: options,
|
||||||
submitBtnDisabled: options == null
|
submitBtnDisabled: options == null
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
submit = () => {
|
submit = () => {
|
||||||
if (this.state.selectedOptions) {
|
if (this.state.selectedUsers) {
|
||||||
const receiver = this.state.selectedOptions[0].email;
|
const receiver = this.state.selectedUsers[0].email;
|
||||||
this.props.transferGroup(receiver);
|
this.props.transferGroup(receiver);
|
||||||
this.props.toggleDialog();
|
this.props.toggleDialog();
|
||||||
}
|
}
|
||||||
@@ -49,10 +48,10 @@ class SysAdminTransferGroupDialog extends React.Component {
|
|||||||
</SeahubModalHeader>
|
</SeahubModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
ref={this.userSelect}
|
|
||||||
isMulti={false}
|
isMulti={false}
|
||||||
placeholder={gettext('Select a user')}
|
placeholder={gettext('Select a user')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
@@ -1,62 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Button, Modal, ModalBody, ModalFooter } from 'reactstrap';
|
|
||||||
import { gettext } from '../../../utils/constants';
|
|
||||||
import UserSelect from '../../user-select';
|
|
||||||
import SeahubModalHeader from '@/components/common/seahub-modal-header';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
repoName: PropTypes.string.isRequired,
|
|
||||||
toggle: PropTypes.func.isRequired,
|
|
||||||
submit: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
class SysAdminRepoTransferDialog extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
selectedOption: null,
|
|
||||||
errorMsg: [],
|
|
||||||
};
|
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
|
||||||
this.setState({ selectedOption: option });
|
|
||||||
};
|
|
||||||
|
|
||||||
submit = () => {
|
|
||||||
let user = this.state.selectedOption;
|
|
||||||
this.props.submit(user);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const repoName = this.props.repoName;
|
|
||||||
const innerSpan = '<span class="op-target" title=' + repoName + '>' + repoName + '</span>';
|
|
||||||
let msg = gettext('Transfer Library {library_name}');
|
|
||||||
let message = msg.replace('{library_name}', innerSpan);
|
|
||||||
return (
|
|
||||||
<Modal isOpen={true} toggle={this.props.toggle}>
|
|
||||||
<SeahubModalHeader toggle={this.props.toggle}>
|
|
||||||
<div dangerouslySetInnerHTML={{ __html: message }} />
|
|
||||||
</SeahubModalHeader>
|
|
||||||
<ModalBody>
|
|
||||||
<UserSelect
|
|
||||||
ref={this.userSelect}
|
|
||||||
isMulti={false}
|
|
||||||
placeholder={gettext('Search users')}
|
|
||||||
onSelectChange={this.handleSelectChange}
|
|
||||||
/>
|
|
||||||
</ModalBody>
|
|
||||||
<ModalFooter>
|
|
||||||
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
|
||||||
<Button color="primary" onClick={this.submit}>{gettext('Submit')}</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SysAdminRepoTransferDialog.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default SysAdminRepoTransferDialog;
|
|
@@ -15,7 +15,6 @@ class UserItem extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
isOperationShow: false
|
isOperationShow: false
|
||||||
};
|
};
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseEnter = () => {
|
onMouseEnter = () => {
|
||||||
@@ -115,7 +114,7 @@ class SysAdminShareToUser extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
errorMsg: [],
|
errorMsg: [],
|
||||||
permission: 'rw',
|
permission: 'rw',
|
||||||
sharedItems: []
|
sharedItems: []
|
||||||
@@ -127,8 +126,8 @@ class SysAdminShareToUser extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (options) => {
|
||||||
this.setState({ selectedOption: option });
|
this.setState({ selectedUsers: options });
|
||||||
this.options = [];
|
this.options = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,9 +150,9 @@ class SysAdminShareToUser extends React.Component {
|
|||||||
shareToUser = () => {
|
shareToUser = () => {
|
||||||
let users = [];
|
let users = [];
|
||||||
let repoID = this.props.repoID;
|
let repoID = this.props.repoID;
|
||||||
if (this.state.selectedOption && this.state.selectedOption.length > 0) {
|
if (this.state.selectedUsers && this.state.selectedUsers.length > 0) {
|
||||||
for (let i = 0; i < this.state.selectedOption.length; i ++) {
|
for (let i = 0; i < this.state.selectedUsers.length; i ++) {
|
||||||
users[i] = this.state.selectedOption[i].email;
|
users[i] = this.state.selectedUsers[i].email;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
systemAdminAPI.sysAdminAddRepoSharedItem(repoID, 'user', users, this.state.permission).then(res => {
|
systemAdminAPI.sysAdminAddRepoSharedItem(repoID, 'user', users, this.state.permission).then(res => {
|
||||||
@@ -167,10 +166,9 @@ class SysAdminShareToUser extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
errorMsg: errorMsg,
|
errorMsg: errorMsg,
|
||||||
sharedItems: this.state.sharedItems.concat(newItems),
|
sharedItems: this.state.sharedItems.concat(newItems),
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
permission: 'rw',
|
permission: 'rw',
|
||||||
});
|
});
|
||||||
this.userSelect.current.clearSelect();
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
let message = gettext('Library can not be shared to owner.');
|
let message = gettext('Library can not be shared to owner.');
|
||||||
@@ -178,7 +176,7 @@ class SysAdminShareToUser extends React.Component {
|
|||||||
errMessage.push(message);
|
errMessage.push(message);
|
||||||
this.setState({
|
this.setState({
|
||||||
errorMsg: errMessage,
|
errorMsg: errMessage,
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -236,10 +234,10 @@ class SysAdminShareToUser extends React.Component {
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
ref={this.userSelect}
|
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
placeholder={gettext('Search users')}
|
placeholder={gettext('Search users')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
@@ -34,22 +34,26 @@ class TransferDialog extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
options: [],
|
options: [],
|
||||||
selectedOption: null,
|
selectedOption: null,
|
||||||
|
selectedUsers: [],
|
||||||
errorMsg: [],
|
errorMsg: [],
|
||||||
transferToUser: true,
|
transferToUser: true,
|
||||||
transferToGroup: false,
|
transferToGroup: false,
|
||||||
reshare: false,
|
reshare: false,
|
||||||
activeTab: !this.props.isDepAdminTransfer ? TRANS_USER : TRANS_DEPART
|
activeTab: !this.props.isDepAdminTransfer ? TRANS_USER : TRANS_DEPART
|
||||||
};
|
};
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (option) => {
|
||||||
this.setState({ selectedOption: option });
|
this.setState({ selectedOption: option });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onUsersChange = (selectedUsers) => {
|
||||||
|
this.setState({ selectedUsers });
|
||||||
|
};
|
||||||
|
|
||||||
submit = () => {
|
submit = () => {
|
||||||
const { activeTab, reshare, selectedOption } = this.state;
|
const { activeTab, reshare, selectedOption, selectedUsers } = this.state;
|
||||||
const email = activeTab === TRANS_DEPART ? selectedOption.email : selectedOption[0].email;
|
const email = activeTab === TRANS_DEPART ? selectedOption.email : selectedUsers[0].email;
|
||||||
this.props.onTransferRepo(email, reshare);
|
this.props.onTransferRepo(email, reshare);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -106,6 +110,7 @@ class TransferDialog extends React.Component {
|
|||||||
activeTab: tab,
|
activeTab: tab,
|
||||||
reshare: false,
|
reshare: false,
|
||||||
selectedOption: null,
|
selectedOption: null,
|
||||||
|
selectedUsers: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -158,10 +163,10 @@ class TransferDialog extends React.Component {
|
|||||||
<TabPane tabId="transUser" role="tabpanel" id="transfer-user-panel">
|
<TabPane tabId="transUser" role="tabpanel" id="transfer-user-panel">
|
||||||
<Label className='transfer-repo-label'>{gettext('Users')}</Label>
|
<Label className='transfer-repo-label'>{gettext('Users')}</Label>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
ref={this.userSelect}
|
|
||||||
isMulti={false}
|
isMulti={false}
|
||||||
placeholder={gettext('Select a user')}
|
placeholder={gettext('Select a user')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.onUsersChange}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
<Switch
|
<Switch
|
||||||
checked={reshare}
|
checked={reshare}
|
||||||
@@ -207,13 +212,19 @@ class TransferDialog extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { selectedOption } = this.state;
|
const { selectedOption, activeTab } = this.state;
|
||||||
const { itemName: repoName } = this.props;
|
const { itemName: repoName } = this.props;
|
||||||
let title = gettext('Transfer Library {library_name}');
|
let title = gettext('Transfer Library {library_name}');
|
||||||
title = title.replace('{library_name}', '<span class="op-target text-truncate mx-1">' + Utils.HTMLescape(repoName) + '</span>');
|
title = title.replace('{library_name}', '<span class="op-target text-truncate mx-1">' + Utils.HTMLescape(repoName) + '</span>');
|
||||||
let buttonDisabled = false;
|
let buttonDisabled = false;
|
||||||
if (selectedOption === null || (Array.isArray(selectedOption) && selectedOption.length === 0)) {
|
if (activeTab === TRANS_DEPART) {
|
||||||
buttonDisabled = true;
|
if (selectedOption === null || (Array.isArray(selectedOption) && selectedOption.length === 0)) {
|
||||||
|
buttonDisabled = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.state.selectedUsers.length === 0) {
|
||||||
|
buttonDisabled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={true} style={{ maxWidth: '720px' }} toggle={this.props.toggleDialog} className="transfer-dialog">
|
<Modal isOpen={true} style={{ maxWidth: '720px' }} toggle={this.props.toggleDialog} className="transfer-dialog">
|
||||||
|
@@ -21,21 +21,21 @@ class TransferGroupDialog extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedOption: null
|
selectedUsers: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (option) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedOption: option
|
selectedUsers: option
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
transferGroup = () => {
|
transferGroup = () => {
|
||||||
let selectedOption = this.state.selectedOption;
|
let selectedUsers = this.state.selectedUsers;
|
||||||
let email;
|
let email;
|
||||||
if (selectedOption && selectedOption[0]) {
|
if (selectedUsers && selectedUsers[0]) {
|
||||||
email = selectedOption[0].email;
|
email = selectedUsers[0].email;
|
||||||
}
|
}
|
||||||
if (!email) {
|
if (!email) {
|
||||||
return false;
|
return false;
|
||||||
@@ -61,10 +61,10 @@ class TransferGroupDialog extends React.Component {
|
|||||||
<ModalBody>
|
<ModalBody>
|
||||||
<p>{gettext('Transfer group to')}</p>
|
<p>{gettext('Transfer group to')}</p>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
ref={this.userSelect}
|
|
||||||
isMulti={false}
|
isMulti={false}
|
||||||
placeholder={gettext('Please enter 1 or more character')}
|
placeholder={gettext('Please enter 1 or more character')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
@@ -27,14 +27,13 @@ class ManageMembersDialog extends React.Component {
|
|||||||
page: 1,
|
page: 1,
|
||||||
perPage: 100,
|
perPage: 100,
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
errMessage: [],
|
errMessage: [],
|
||||||
isItemFreezed: false,
|
isItemFreezed: false,
|
||||||
searchActive: false,
|
searchActive: false,
|
||||||
keyword: '',
|
keyword: '',
|
||||||
membersFound: []
|
membersFound: []
|
||||||
};
|
};
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -66,23 +65,22 @@ class ManageMembersDialog extends React.Component {
|
|||||||
|
|
||||||
onSelectChange = (option) => {
|
onSelectChange = (option) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedOption: option,
|
selectedUsers: option,
|
||||||
errMessage: [],
|
errMessage: [],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
addGroupMember = () => {
|
addGroupMember = () => {
|
||||||
let emails = [];
|
let emails = [];
|
||||||
for (let i = 0; i < this.state.selectedOption.length; i++) {
|
for (let i = 0; i < this.state.selectedUsers.length; i++) {
|
||||||
emails.push(this.state.selectedOption[i].email);
|
emails.push(this.state.selectedUsers[i].email);
|
||||||
}
|
}
|
||||||
seafileAPI.addGroupMembers(this.props.groupID, emails).then((res) => {
|
seafileAPI.addGroupMembers(this.props.groupID, emails).then((res) => {
|
||||||
const newMembers = res.data.success;
|
const newMembers = res.data.success;
|
||||||
this.setState({
|
this.setState({
|
||||||
groupMembers: [].concat(newMembers, this.state.groupMembers),
|
groupMembers: [].concat(newMembers, this.state.groupMembers),
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
});
|
});
|
||||||
this.userSelect.current.clearSelect();
|
|
||||||
if (res.data.failed.length > 0) {
|
if (res.data.failed.length > 0) {
|
||||||
this.setState({
|
this.setState({
|
||||||
errMessage: res.data.failed
|
errMessage: res.data.failed
|
||||||
@@ -186,14 +184,14 @@ class ManageMembersDialog extends React.Component {
|
|||||||
<UserSelect
|
<UserSelect
|
||||||
placeholder={gettext('Search users')}
|
placeholder={gettext('Search users')}
|
||||||
onSelectChange={this.onSelectChange}
|
onSelectChange={this.onSelectChange}
|
||||||
ref={this.userSelect}
|
selectedUsers={this.state.selectedUsers}
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
className="add-members-select"
|
className="add-members-select"
|
||||||
/>
|
/>
|
||||||
{showDeptBtn &&
|
{showDeptBtn &&
|
||||||
<span onClick={this.onClickDeptBtn} className="sf3-font sf3-font-invite-visitors toggle-detail-btn"></span>
|
<span onClick={this.onClickDeptBtn} className="sf3-font sf3-font-invite-visitors toggle-detail-btn"></span>
|
||||||
}
|
}
|
||||||
{this.state.selectedOption ?
|
{this.state.selectedUsers.length > 0 ?
|
||||||
<Button color="primary" onClick={this.addGroupMember}>{gettext('Submit')}</Button> :
|
<Button color="primary" onClick={this.addGroupMember}>{gettext('Submit')}</Button> :
|
||||||
<Button color="primary" disabled>{gettext('Submit')}</Button>
|
<Button color="primary" disabled>{gettext('Submit')}</Button>
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,6 @@ class UserItem extends React.Component {
|
|||||||
isHighlighted: false,
|
isHighlighted: false,
|
||||||
isOperationShow: false
|
isOperationShow: false
|
||||||
};
|
};
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseEnter = () => {
|
onMouseEnter = () => {
|
||||||
@@ -89,7 +88,7 @@ class LinkAuthenticatedUsers extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
authUsers: []
|
authUsers: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -110,11 +109,11 @@ class LinkAuthenticatedUsers extends React.Component {
|
|||||||
|
|
||||||
addLinkAuthUsers = () => {
|
addLinkAuthUsers = () => {
|
||||||
const { linkToken, path } = this.props;
|
const { linkToken, path } = this.props;
|
||||||
const { selectedOption, authUsers } = this.state;
|
const { selectedUsers, authUsers } = this.state;
|
||||||
if (!selectedOption || !selectedOption.length) {
|
if (!selectedUsers || !selectedUsers.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const users = selectedOption.map((item, index) => item.email);
|
const users = selectedUsers.map((item, index) => item.email);
|
||||||
shareLinkAPI.addShareLinkAuthUsers(linkToken, users, path).then(res => {
|
shareLinkAPI.addShareLinkAuthUsers(linkToken, users, path).then(res => {
|
||||||
const { success, failed } = res.data;
|
const { success, failed } = res.data;
|
||||||
if (success.length) {
|
if (success.length) {
|
||||||
@@ -130,9 +129,8 @@ class LinkAuthenticatedUsers extends React.Component {
|
|||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
authUsers: success.concat(authUsers),
|
authUsers: success.concat(authUsers),
|
||||||
selectedOption: null
|
selectedUsers: []
|
||||||
});
|
});
|
||||||
this.userSelect.current.clearSelect();
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
toaster.danger(errMessage);
|
toaster.danger(errMessage);
|
||||||
@@ -160,7 +158,7 @@ class LinkAuthenticatedUsers extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (option) => {
|
||||||
this.setState({ selectedOption: option });
|
this.setState({ selectedUsers: option });
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -193,10 +191,10 @@ class LinkAuthenticatedUsers extends React.Component {
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
ref={this.userSelect}
|
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
placeholder={gettext('Search users')}
|
placeholder={gettext('Search users')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
@@ -47,10 +47,9 @@ class LinkCreation extends React.Component {
|
|||||||
currentPermission: props.currentPermission,
|
currentPermission: props.currentPermission,
|
||||||
|
|
||||||
currentScope: 'all_users',
|
currentScope: 'all_users',
|
||||||
selectedOption: null,
|
selectedUsers: [],
|
||||||
inputEmails: ''
|
inputEmails: ''
|
||||||
};
|
};
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setExpType = (e) => {
|
setExpType = (e) => {
|
||||||
@@ -128,9 +127,9 @@ class LinkCreation extends React.Component {
|
|||||||
const autoGeneratePassword = shareLinkForceUsePassword || isShowPasswordInput;
|
const autoGeneratePassword = shareLinkForceUsePassword || isShowPasswordInput;
|
||||||
request = seafileAPI.batchCreateMultiShareLink(repoID, itemPath, linkAmount, autoGeneratePassword, expirationTime, permissions);
|
request = seafileAPI.batchCreateMultiShareLink(repoID, itemPath, linkAmount, autoGeneratePassword, expirationTime, permissions);
|
||||||
} else {
|
} else {
|
||||||
const { currentScope, selectedOption, inputEmails } = this.state;
|
const { currentScope, selectedUsers, inputEmails } = this.state;
|
||||||
if (currentScope === 'specific_users' && selectedOption) {
|
if (currentScope === 'specific_users' && selectedUsers) {
|
||||||
users = selectedOption.map((item, index) => item.email);
|
users = selectedUsers.map((item, index) => item.email);
|
||||||
}
|
}
|
||||||
if (currentScope === 'specific_emails' && inputEmails) {
|
if (currentScope === 'specific_emails' && inputEmails) {
|
||||||
users = inputEmails;
|
users = inputEmails;
|
||||||
@@ -264,11 +263,11 @@ class LinkCreation extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
setScope = (e) => {
|
setScope = (e) => {
|
||||||
this.setState({ currentScope: e.target.value, selectedOption: null, inputEmails: '' });
|
this.setState({ currentScope: e.target.value, selectedUsers: [], inputEmails: '' });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSelectChange = (option) => {
|
handleSelectChange = (option) => {
|
||||||
this.setState({ selectedOption: option });
|
this.setState({ selectedUsers: option });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleInputChange = (e) => {
|
handleInputChange = (e) => {
|
||||||
@@ -390,10 +389,10 @@ class LinkCreation extends React.Component {
|
|||||||
</Label>
|
</Label>
|
||||||
{this.state.currentScope === 'specific_users' &&
|
{this.state.currentScope === 'specific_users' &&
|
||||||
<UserSelect
|
<UserSelect
|
||||||
ref={this.userSelect}
|
|
||||||
isMulti={true}
|
isMulti={true}
|
||||||
placeholder={gettext('Search users')}
|
placeholder={gettext('Search users')}
|
||||||
onSelectChange={this.handleSelectChange}
|
onSelectChange={this.handleSelectChange}
|
||||||
|
selectedUsers={this.state.selectedUsers}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
59
frontend/src/components/user-item/index.css
Normal file
59
frontend/src/components/user-item/index.css
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
.user-item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 2px 10px 2px 0;
|
||||||
|
padding: 0 8px 0 2px;
|
||||||
|
height: 20px;
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #eaeaea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-item .user-avatar,
|
||||||
|
.user-item .user-name,
|
||||||
|
.user-item .user-remove {
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-item .user-avatar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transform: translateY(0);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-item .user-name {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-item .user-avatar img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-item .user-remove {
|
||||||
|
display: inline-block;
|
||||||
|
width: 14px;
|
||||||
|
margin: 0 -2px 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-item .user-remove .sf3-font {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909090;
|
||||||
|
transform: scale(.8);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-item .user-remove .sf3-font:hover {
|
||||||
|
color: #666666;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-item .user-option-email {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
54
frontend/src/components/user-item/index.js
Normal file
54
frontend/src/components/user-item/index.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { gettext, enableShowContactEmailWhenSearchUser, enableShowLoginIDWhenSearchUser } from '../../utils/constants';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
user: PropTypes.shape({
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
avatar_url: PropTypes.string.isRequired,
|
||||||
|
email: PropTypes.string,
|
||||||
|
contact_email: PropTypes.string,
|
||||||
|
login_id: PropTypes.string,
|
||||||
|
}),
|
||||||
|
className: PropTypes.string,
|
||||||
|
enableDeleteUser: PropTypes.bool,
|
||||||
|
onDeleteUser: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
class UserItem extends React.Component {
|
||||||
|
|
||||||
|
onDeleteUser = (event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event && event.nativeEvent.stopImmediatePropagation();
|
||||||
|
this.props.onDeleteUser(this.props.user);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { className, user, enableDeleteUser } = this.props;
|
||||||
|
const { name, avatar_url, contact_email, login_id } = user;
|
||||||
|
return (
|
||||||
|
<div className={classnames('user-item', className)} title={name}>
|
||||||
|
<span className="user-avatar">
|
||||||
|
<img className="user-avatar-icon" alt={name} src={avatar_url} />
|
||||||
|
</span>
|
||||||
|
<div className="d-flex align-items-center">
|
||||||
|
<span className="user-name">{name}</span>
|
||||||
|
{(enableShowContactEmailWhenSearchUser && !enableDeleteUser) && <span className="user-option-email">({contact_email})</span>}
|
||||||
|
{(enableShowLoginIDWhenSearchUser && !enableDeleteUser) && <span className="user-option-email">({login_id})</span>}
|
||||||
|
</div>
|
||||||
|
{enableDeleteUser && (
|
||||||
|
<span className="user-remove ml-2" onClick={this.onDeleteUser} title={gettext('Remove')}>
|
||||||
|
<i className="sf3-font sf3-font-x-01" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UserItem.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default UserItem;
|
@@ -1,114 +1,265 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import AsyncSelect from 'react-select/async';
|
import classnames from 'classnames';
|
||||||
|
import { Popover } from 'reactstrap';
|
||||||
import { seafileAPI } from '../utils/seafile-api';
|
import { seafileAPI } from '../utils/seafile-api';
|
||||||
import { gettext, enableShowContactEmailWhenSearchUser, enableShowLoginIDWhenSearchUser } from '../utils/constants';
|
import { gettext } from '../utils/constants';
|
||||||
import { Utils } from '../utils/utils';
|
import { Utils } from '../utils/utils';
|
||||||
import toaster from './toast';
|
import toaster from './toast';
|
||||||
import { UserSelectStyle, NoOptionsStyle } from './common/select';
|
import KeyCodes from '../constants/keyCodes';
|
||||||
|
import SearchInput from './search-input';
|
||||||
|
import UserItem from '../components/user-item';
|
||||||
|
import ClickOutside from './click-outside';
|
||||||
|
|
||||||
import '../css/user-select.css';
|
import '../css/user-select.css';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
placeholder: PropTypes.string.isRequired,
|
placeholder: PropTypes.string.isRequired,
|
||||||
onSelectChange: PropTypes.func.isRequired,
|
onSelectChange: PropTypes.func.isRequired,
|
||||||
isMulti: PropTypes.bool.isRequired,
|
isMulti: PropTypes.bool,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class UserSelect extends React.Component {
|
class UserSelect extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.options = [];
|
|
||||||
this.finalValue = '';
|
|
||||||
this.state = {
|
this.state = {
|
||||||
searchValue: ''
|
maxItemNum: 0,
|
||||||
|
itemHeight: 0,
|
||||||
|
searchedUsers: [],
|
||||||
|
searchValue: '',
|
||||||
|
highlightIndex: -1,
|
||||||
};
|
};
|
||||||
this.userSelect = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onInputChange = (searchValue) => {
|
onValueChanged = (newSearchValue) => {
|
||||||
if (!this.props.isMulti && searchValue.trim()) {
|
this.setState({
|
||||||
this.handleSelectChange(null);
|
searchValue: newSearchValue
|
||||||
this.clearSelect();
|
});
|
||||||
}
|
const searchValue = newSearchValue.trim();
|
||||||
this.setState({ searchValue });
|
if (searchValue.length === 0) {
|
||||||
};
|
this.setState({
|
||||||
|
searchedUsers: [],
|
||||||
handleSelectChange = (option) => {
|
highlightIndex: -1,
|
||||||
this.options = [];
|
});
|
||||||
this.props.onSelectChange(option);
|
} else {
|
||||||
};
|
seafileAPI.searchUsers(newSearchValue.trim()).then((res) => {
|
||||||
|
this.setState({
|
||||||
loadOptions = (input, callback) => {
|
searchedUsers: res.data.users,
|
||||||
const value = input.trim();
|
highlightIndex: res.data.users.length > 0 ? 0 : -1,
|
||||||
this.finalValue = value;
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.finalValue === value && value.length > 0) {
|
|
||||||
seafileAPI.searchUsers(value).then((res) => {
|
|
||||||
this.options = [];
|
|
||||||
for (let i = 0 ; i < res.data.users.length; i++) {
|
|
||||||
const item = res.data.users[i];
|
|
||||||
let obj = {};
|
|
||||||
obj.value = item.name;
|
|
||||||
obj.email = item.email;
|
|
||||||
obj.label = (enableShowContactEmailWhenSearchUser || enableShowLoginIDWhenSearchUser) ? (
|
|
||||||
<div className="d-flex">
|
|
||||||
<img src={item.avatar_url} className="avatar" width="24" alt="" />
|
|
||||||
<div className="ml-2">
|
|
||||||
<span className="user-option-name">{item.name}</span><br />
|
|
||||||
{enableShowContactEmailWhenSearchUser && <span className="user-option-email">{item.contact_email}</span>}
|
|
||||||
{enableShowLoginIDWhenSearchUser && <span className="user-option-email">{item.login_id}</span>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<React.Fragment>
|
|
||||||
<img src={item.avatar_url} className="select-module select-module-icon avatar" alt=""/>
|
|
||||||
<span className='select-module select-module-name'>{item.name}</span>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
this.options.push(obj);
|
|
||||||
}
|
|
||||||
callback(this.options);
|
|
||||||
}).catch(error => {
|
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
|
||||||
toaster.danger(errMessage);
|
|
||||||
});
|
});
|
||||||
}
|
}).catch(error => {
|
||||||
}, 1000);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
clearSelect = () => {
|
componentDidMount() {
|
||||||
this.userSelect.current.onChange([], { action: 'clear' });
|
if (this.ref) {
|
||||||
|
const { bottom } = this.ref.getBoundingClientRect();
|
||||||
|
if (bottom > window.innerHeight) {
|
||||||
|
this.ref.style.top = `${window.innerHeight - bottom}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.container && this.userItem) {
|
||||||
|
this.setState({
|
||||||
|
maxItemNum: this.getMaxItemNum(),
|
||||||
|
itemHeight: parseInt(getComputedStyle(this.userItem, null).height)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
document.addEventListener('keydown', this.onHotKey, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
document.removeEventListener('keydown', this.onHotKey, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickOutside = (e) => {
|
||||||
|
if (e.target.id !== 'user-select' && this.state.isPopoverOpen) {
|
||||||
|
this.setState({
|
||||||
|
isPopoverOpen: false,
|
||||||
|
searchedUsers: [],
|
||||||
|
searchValue: '',
|
||||||
|
highlightIndex: -1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getMaxItemNum = () => {
|
||||||
|
let userContainerStyle = getComputedStyle(this.container, null);
|
||||||
|
let userItemStyle = getComputedStyle(this.userItem, null);
|
||||||
|
let maxContainerItemNum = Math.floor(parseInt(userContainerStyle.maxHeight) / parseInt(userItemStyle.height));
|
||||||
|
return maxContainerItemNum - 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
onHotKey = (e) => {
|
||||||
|
if (e.keyCode === KeyCodes.Enter) {
|
||||||
|
this.onEnter(e);
|
||||||
|
} else if (e.keyCode === KeyCodes.UpArrow) {
|
||||||
|
this.onUpArrow(e);
|
||||||
|
} else if (e.keyCode === KeyCodes.DownArrow) {
|
||||||
|
this.onDownArrow(e);
|
||||||
|
} else if (e.keyCode === KeyCodes.Escape) {
|
||||||
|
this.onEsc(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onEnter = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
let user;
|
||||||
|
if (this.state.searchedUsers.length === 1) {
|
||||||
|
user = this.state.searchedUsers[0];
|
||||||
|
} else if (this.state.highlightIndex > -1) {
|
||||||
|
user = this.state.searchedUsers[this.state.highlightIndex];
|
||||||
|
}
|
||||||
|
if (user) {
|
||||||
|
this.onUserClick(user);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onUpArrow = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
let { highlightIndex, maxItemNum, itemHeight } = this.state;
|
||||||
|
if (highlightIndex > 0) {
|
||||||
|
this.setState({ highlightIndex: highlightIndex - 1 }, () => {
|
||||||
|
if (highlightIndex < this.state.searchedUsers.length - maxItemNum) {
|
||||||
|
this.container.scrollTop -= itemHeight;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({ highlightIndex: this.state.searchedUsers.length - 1 }, () => {
|
||||||
|
this.container.scrollTop = this.container.scrollHeight;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onDownArrow = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
let { highlightIndex, maxItemNum, itemHeight } = this.state;
|
||||||
|
if (highlightIndex < this.state.searchedUsers.length - 1) {
|
||||||
|
this.setState({ highlightIndex: highlightIndex + 1 }, () => {
|
||||||
|
if (highlightIndex >= maxItemNum) {
|
||||||
|
this.container.scrollTop += itemHeight;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({ highlightIndex: 0 }, () => {
|
||||||
|
this.container.scrollTop = 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onEsc = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.setState({ isPopoverOpen: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
onUserClick = (user) => {
|
||||||
|
const { isMulti = true } = this.props;
|
||||||
|
let selectedUsers = this.props.selectedUsers.slice(0);
|
||||||
|
if (isMulti) {
|
||||||
|
const index = selectedUsers.findIndex(item => item.email === user.email);
|
||||||
|
if (index > -1) {
|
||||||
|
selectedUsers.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
selectedUsers.push(user);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedUsers = [user];
|
||||||
|
}
|
||||||
|
this.props.onSelectChange(selectedUsers);
|
||||||
|
};
|
||||||
|
|
||||||
|
onKeyDown = (e) => {
|
||||||
|
if (e.keyCode === KeyCodes.LeftArrow || e.keyCode === KeyCodes.RightArrow) {
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onDeleteSelectedCollaborator = (user) => {
|
||||||
|
const { selectedUsers = [] } = this.props;
|
||||||
|
const newSelectedCollaborator = selectedUsers.filter(item => item.email !== user.email);
|
||||||
|
this.props.onSelectChange(newSelectedCollaborator);
|
||||||
|
};
|
||||||
|
|
||||||
|
onTogglePopover = () => {
|
||||||
|
this.setState({ isPopoverOpen: !this.state.isPopoverOpen });
|
||||||
|
if (!this.state.isPopoverOpen) {
|
||||||
|
this.onValueChanged(this.state.searchValue);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const searchValue = this.state.searchValue;
|
const { searchValue, highlightIndex, searchedUsers } = this.state;
|
||||||
|
const { className = '', selectedUsers = [] } = this.props;
|
||||||
return (
|
return (
|
||||||
<AsyncSelect
|
<ClickOutside onClickOutside={this.onClickOutside}>
|
||||||
isClearable
|
<>
|
||||||
classNamePrefix
|
<div className={classnames('selected-user-item-container form-control d-flex align-items-center', className, { 'focus': this.state.isPopoverOpen })} id="user-select" onClick={this.onTogglePopover}>
|
||||||
components={{
|
{selectedUsers.map((user, index) => {
|
||||||
NoOptionsMessage: (props) => {
|
return (
|
||||||
return (
|
<UserItem
|
||||||
<div {...props.innerProps} style={NoOptionsStyle}>
|
key={index}
|
||||||
{searchValue ? gettext('User not found') : gettext('Enter characters to start searching')}
|
user={user}
|
||||||
|
enableDeleteUser={true}
|
||||||
|
onDeleteUser={this.onDeleteSelectedCollaborator}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{selectedUsers.length === 0 && (
|
||||||
|
<div className="user-select-placeholder">
|
||||||
|
{this.props.placeholder || gettext('Select users')}
|
||||||
</div>
|
</div>
|
||||||
);
|
)}
|
||||||
}
|
</div>
|
||||||
}}
|
<Popover
|
||||||
isMulti={true}
|
placement="bottom-start"
|
||||||
loadOptions={this.loadOptions}
|
isOpen={this.state.isPopoverOpen}
|
||||||
onChange={this.handleSelectChange}
|
target={'user-select'}
|
||||||
onInputChange={this.onInputChange}
|
hideArrow={true}
|
||||||
placeholder={this.props.placeholder}
|
fade={false}
|
||||||
className={`user-select ${this.props.className || ''}`}
|
className="user-select-popover"
|
||||||
value={this.props.value}
|
>
|
||||||
ref={this.userSelect}
|
<div className="user-select-container" ref={ref => this.ref = ref} onMouseDown={e => e.stopPropagation()}>
|
||||||
styles={UserSelectStyle}
|
<div className="user-search-container">
|
||||||
/>
|
<SearchInput
|
||||||
|
autoFocus={true}
|
||||||
|
placeholder={this.props.placeholder || gettext('Search users')}
|
||||||
|
value={searchValue}
|
||||||
|
onChange={this.onValueChanged}
|
||||||
|
onKeyDown={this.onKeyDown}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="user-list-container" ref={ref => this.container = ref}>
|
||||||
|
{searchedUsers.length > 0 && (
|
||||||
|
searchedUsers.map((user, index) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={user.email}
|
||||||
|
className={classnames('user-item-container', { 'user-item-container-highlight': index === highlightIndex })}
|
||||||
|
ref={ref => this.userItem = ref}
|
||||||
|
onClick={this.onUserClick.bind(this, user)}
|
||||||
|
>
|
||||||
|
<UserItem user={user} enableDeleteUser={false} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
{searchedUsers.length === 0 &&
|
||||||
|
<div className="no-search-result">
|
||||||
|
{searchValue ? gettext('User not found') : gettext('Enter characters to start searching')}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
</>
|
||||||
|
</ClickOutside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,32 +1,59 @@
|
|||||||
.user-option-name {
|
.selected-user-item-container {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
min-height: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-user-item-container .user-select-placeholder {
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-select-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-select-container .user-search-container {
|
||||||
|
padding: 10px 10px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-select-container .user-search-container input {
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-select-container .user-list-container {
|
||||||
|
min-height: 160px;
|
||||||
|
max-height: 200px;
|
||||||
|
margin: 10px 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.user-select-container .user-list-container .user-item-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 30px;
|
||||||
|
padding: 0 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-option-email {
|
.user-select-container .user-list-container .user-item-container:hover,
|
||||||
font-size: 12px;
|
.user-select-container .user-list-container .user-item-container-highlight {
|
||||||
|
background: #f5f5f5;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dropdown menu avatar is 24*24px, selection box avatar is 16*16px */
|
.no-search-result {
|
||||||
.user-select .true__value-container .select-module.select-module-icon.avatar {
|
color: #666666;
|
||||||
height: 16px;
|
font-size: 14px;
|
||||||
width: 16px;
|
padding-left: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-select .true__value-container .true__multi-value__label {
|
.user-select-popover .popover {
|
||||||
padding: 0px;
|
width: 385px;
|
||||||
|
max-width: 385px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-select .true__value-container .true__multi-value__label .select-module.select-module-icon.avatar {
|
.user-select-popover .popover .user-item {
|
||||||
transform: translateY(-2px);
|
background: transparent;
|
||||||
}
|
|
||||||
|
|
||||||
.user-select .true__value-container .select-module.select-module-name {
|
|
||||||
font-size: 13px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
flex: 1 1;
|
|
||||||
line-height: 20px;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user