mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-27 07:44:50 +00:00
Change transfer repo dialog UI (#7801)
* 01 change transfer repo to user * 01 change transfer repo to department
This commit is contained in:
@@ -47,6 +47,19 @@
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.seafile-customize-select .selected-option .selected-option-show-container {
|
||||
background: #F2F2F2;
|
||||
border-radius: 10px;
|
||||
padding-right: 6px;
|
||||
padding-left: 12px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.seafile-customize-select .selected-option .selected-option-show-container .sf3-font-x-01 {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.seafile-customize-select .selected-option .custom-select-dropdown-icon {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
@@ -97,4 +110,5 @@
|
||||
line-height: 1;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
color: #999;
|
||||
}
|
||||
|
@@ -88,7 +88,7 @@ class CustomizeSelect extends Component {
|
||||
|
||||
render() {
|
||||
const { className, value, options, placeholder, searchable, searchPlaceholder, noOptionsPlaceholder,
|
||||
readOnly, isInModal, addOptionAble, component } = this.props;
|
||||
readOnly, isInModal, addOptionAble, component, enableDeleteSelected } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -101,9 +101,16 @@ class CustomizeSelect extends Component {
|
||||
onClick={this.onSelectToggle}>
|
||||
<div className="selected-option">
|
||||
{value && value.label ?
|
||||
<span className="selected-option-show">{value.label}</span>
|
||||
:
|
||||
<span className="select-placeholder">{placeholder}</span>
|
||||
(enableDeleteSelected ?
|
||||
<span className="selected-option-show-container">
|
||||
<span className='selected-option-show'>{value.label}</span>
|
||||
<span className='selected-option-delete ml-1' onClick={this.props.deleteSelected}>
|
||||
<i className="sf3-font sf3-font-x-01" aria-hidden="true"></i>
|
||||
</span>
|
||||
</span>
|
||||
: <span className="selected-option-show">{value.label}</span>
|
||||
)
|
||||
: <span className="select-placeholder">{placeholder}</span>
|
||||
}
|
||||
{this.renderDropDownIcon()}
|
||||
</div>
|
||||
@@ -168,6 +175,8 @@ CustomizeSelect.propTypes = {
|
||||
supportMultipleSelect: PropTypes.bool,
|
||||
isShowSelected: PropTypes.bool,
|
||||
isInModal: PropTypes.bool, // if select component in a modal (option group need ModalPortal to show)
|
||||
enableDeleteSelected: PropTypes.bool,
|
||||
deleteSelected: PropTypes.func,
|
||||
};
|
||||
|
||||
export default CustomizeSelect;
|
||||
|
@@ -56,6 +56,11 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.seafile-select-option .sf2-icon-tick {
|
||||
right: 10px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.seafile-select-option.seafile-select-option-active .select-option-name {
|
||||
color: #fff;
|
||||
}
|
||||
|
@@ -128,7 +128,7 @@ class SelectOptionGroup extends Component {
|
||||
};
|
||||
|
||||
renderOptGroup = (searchVal) => {
|
||||
let { noOptionsPlaceholder, onSelectOption } = this.props;
|
||||
let { noOptionsPlaceholder, onSelectOption, value } = this.props;
|
||||
this.filterOptions = this.props.getFilterOptions(searchVal);
|
||||
if (this.filterOptions.length === 0) {
|
||||
return (
|
||||
@@ -138,6 +138,7 @@ class SelectOptionGroup extends Component {
|
||||
return this.filterOptions.map((opt, i) => {
|
||||
let key = opt.value.column ? opt.value.column.key : i;
|
||||
let isActive = this.state.activeIndex === i;
|
||||
const isSelected = value && opt.value === value.value;
|
||||
return (
|
||||
<Option
|
||||
key={key}
|
||||
@@ -150,6 +151,7 @@ class SelectOptionGroup extends Component {
|
||||
disableHover={this.state.disableHover}
|
||||
>
|
||||
{opt.label}
|
||||
{isSelected && <i className="sf2-icon-tick"></i>}
|
||||
</Option>
|
||||
);
|
||||
});
|
||||
|
@@ -1,9 +1,8 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalBody, ModalFooter,
|
||||
Nav, NavItem, NavLink, TabContent, TabPane, Label } from 'reactstrap';
|
||||
import SeahubModalHeader from '@/components/common/seahub-modal-header';
|
||||
import makeAnimated from 'react-select/animated';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { systemAdminAPI } from '../../utils/system-admin-api';
|
||||
import { orgAdminAPI } from '../../utils/org-admin-api';
|
||||
@@ -11,8 +10,9 @@ import { gettext, isPro, orgID } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import toaster from '../toast';
|
||||
import UserSelect from '../user-select';
|
||||
import { SeahubSelect } from '../common/select';
|
||||
import Switch from '../switch';
|
||||
import CustomizeSelect from '../customize-select';
|
||||
|
||||
import '../../css/transfer-dialog.css';
|
||||
|
||||
const propTypes = {
|
||||
@@ -43,8 +43,19 @@ class TransferDialog extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
handleSelectChange = (option) => {
|
||||
handleSelectDepartment = (value) => {
|
||||
if (this.state.selectedOption && this.state.selectedOption.value === value) {
|
||||
this.setState({ selectedOption: null });
|
||||
} else {
|
||||
const option = this.state.options.find(item => item.value === value);
|
||||
this.setState({ selectedOption: option });
|
||||
}
|
||||
};
|
||||
|
||||
deleteSelectedDepartment = (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.setState({ selectedOption: null });
|
||||
};
|
||||
|
||||
onUsersChange = (selectedUsers) => {
|
||||
@@ -53,7 +64,7 @@ class TransferDialog extends React.Component {
|
||||
|
||||
submit = () => {
|
||||
const { activeTab, reshare, selectedOption, selectedUsers } = this.state;
|
||||
const email = activeTab === TRANS_DEPART ? selectedOption.email : selectedUsers[0].email;
|
||||
const email = activeTab === TRANS_DEPART ? selectedOption.value : selectedUsers[0].email;
|
||||
this.props.onTransferRepo(email, reshare);
|
||||
};
|
||||
|
||||
@@ -89,9 +100,9 @@ class TransferDialog extends React.Component {
|
||||
updateOptions = (departmentsRes) => {
|
||||
const options = departmentsRes.data.map(item => {
|
||||
let option = {
|
||||
value: item.name,
|
||||
email: item.email,
|
||||
value: item.email,
|
||||
label: item.name,
|
||||
name: item.name,
|
||||
};
|
||||
return option;
|
||||
});
|
||||
@@ -128,8 +139,20 @@ class TransferDialog extends React.Component {
|
||||
if (this.props.canTransferToDept != undefined) {
|
||||
canTransferToDept = this.props.canTransferToDept;
|
||||
}
|
||||
|
||||
const { selectedOption } = this.state;
|
||||
let buttonDisabled = false;
|
||||
if (activeTab === TRANS_DEPART) {
|
||||
if (selectedOption === null || (Array.isArray(selectedOption) && selectedOption.length === 0)) {
|
||||
buttonDisabled = true;
|
||||
}
|
||||
} else {
|
||||
if (this.state.selectedUsers.length === 0) {
|
||||
buttonDisabled = true;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Fragment>
|
||||
<>
|
||||
<div className="transfer-dialog-side">
|
||||
<Nav pills>
|
||||
{!this.props.isDepAdminTransfer &&
|
||||
@@ -154,12 +177,13 @@ class TransferDialog extends React.Component {
|
||||
>
|
||||
{gettext('Transfer to department')}
|
||||
</NavLink>
|
||||
</NavItem>}
|
||||
</NavItem>
|
||||
}
|
||||
</Nav>
|
||||
</div>
|
||||
<div className="transfer-dialog-main">
|
||||
<TabContent activeTab={this.state.activeTab}>
|
||||
<Fragment>
|
||||
<>
|
||||
<TabPane tabId="transUser" role="tabpanel" id="transfer-user-panel">
|
||||
<Label className='transfer-repo-label'>{gettext('Users')}</Label>
|
||||
<UserSelect
|
||||
@@ -173,7 +197,7 @@ class TransferDialog extends React.Component {
|
||||
disabled={false}
|
||||
size="large"
|
||||
textPosition="right"
|
||||
className='transfer-repo-reshare-switch w-100 mt-3 mb-1'
|
||||
className='transfer-repo-reshare-switch w-100 mt-6 mb-1'
|
||||
onChange={this.toggleReshareStatus}
|
||||
placeholder={gettext('Keep sharing')}
|
||||
/>
|
||||
@@ -182,50 +206,45 @@ class TransferDialog extends React.Component {
|
||||
{isPro && canTransferToDept &&
|
||||
<TabPane tabId="transDepart" role="tabpanel" id="transfer-depart-panel">
|
||||
<Label className='transfer-repo-label'>{gettext('Departments')}</Label>
|
||||
<SeahubSelect
|
||||
isClearable
|
||||
maxMenuHeight={200}
|
||||
hideSelectedOptions={true}
|
||||
components={makeAnimated()}
|
||||
placeholder={gettext('Select a department')}
|
||||
options={this.state.options}
|
||||
onChange={this.handleSelectChange}
|
||||
<CustomizeSelect
|
||||
readOnly={false}
|
||||
value={this.state.selectedOption}
|
||||
className="transfer-repo-select-department"
|
||||
options={this.state.options}
|
||||
onSelectOption={this.handleSelectDepartment}
|
||||
searchable={true}
|
||||
supportMultipleSelect={false}
|
||||
placeholder={gettext('Select a department')}
|
||||
searchPlaceholder={gettext('Search department')}
|
||||
enableDeleteSelected={true}
|
||||
deleteSelected={this.deleteSelectedDepartment}
|
||||
/>
|
||||
<Switch
|
||||
checked={reshare}
|
||||
disabled={false}
|
||||
size="large"
|
||||
textPosition="right"
|
||||
className='transfer-repo-reshare-switch w-100 mt-3 mb-1'
|
||||
className='transfer-repo-reshare-switch w-100 mt-6 mb-1'
|
||||
onChange={this.toggleReshareStatus}
|
||||
placeholder={gettext('Keep sharing')}
|
||||
/>
|
||||
<div className='tip'>{gettext('If the library is shared to another department, the sharing will be kept.')}</div>
|
||||
</TabPane>}
|
||||
</Fragment>
|
||||
</TabPane>
|
||||
}
|
||||
</>
|
||||
</TabContent>
|
||||
<ModalFooter>
|
||||
<Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button>
|
||||
<Button color="primary" onClick={this.submit} disabled={buttonDisabled}>{gettext('Submit')}</Button>
|
||||
</ModalFooter>
|
||||
</div>
|
||||
</Fragment>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { selectedOption, activeTab } = this.state;
|
||||
const { itemName: repoName } = this.props;
|
||||
let { itemName } = this.props;
|
||||
let title = gettext('Transfer Library {library_name}');
|
||||
title = title.replace('{library_name}', '<span class="op-target text-truncate mx-1">' + Utils.HTMLescape(repoName) + '</span>');
|
||||
let buttonDisabled = false;
|
||||
if (activeTab === TRANS_DEPART) {
|
||||
if (selectedOption === null || (Array.isArray(selectedOption) && selectedOption.length === 0)) {
|
||||
buttonDisabled = true;
|
||||
}
|
||||
} else {
|
||||
if (this.state.selectedUsers.length === 0) {
|
||||
buttonDisabled = true;
|
||||
}
|
||||
}
|
||||
title = title.replace('{library_name}', '<span class="op-target text-truncate mx-1">' + Utils.HTMLescape(itemName) + '</span>');
|
||||
return (
|
||||
<Modal isOpen={true} style={{ maxWidth: '720px' }} toggle={this.props.toggleDialog} className="transfer-dialog">
|
||||
<SeahubModalHeader toggle={this.props.toggleDialog}>
|
||||
@@ -234,10 +253,6 @@ class TransferDialog extends React.Component {
|
||||
<ModalBody className="transfer-dialog-content" role="tablist">
|
||||
{this.renderTransContent()}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="secondary" onClick={this.props.toggleDialog}>{gettext('Cancel')}</Button>
|
||||
<Button color="primary" onClick={this.submit} disabled={buttonDisabled}>{gettext('Submit')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@
|
||||
margin: 2px 10px 2px 0;
|
||||
padding: 0 8px 0 2px;
|
||||
height: 20px;
|
||||
font-size: 13px;
|
||||
border-radius: 10px;
|
||||
background: #eaeaea;
|
||||
}
|
||||
@@ -44,7 +43,6 @@
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: #909090;
|
||||
transform: scale(.8);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,8 @@ import ClickOutside from './click-outside';
|
||||
import '../css/user-select.css';
|
||||
|
||||
const propTypes = {
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string,
|
||||
searchPlaceholder: PropTypes.string,
|
||||
onSelectChange: PropTypes.func.isRequired,
|
||||
isMulti: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
@@ -162,16 +163,20 @@ class UserSelect extends React.Component {
|
||||
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 (isMulti) {
|
||||
if (index > -1) {
|
||||
selectedUsers.splice(index, 1);
|
||||
} else {
|
||||
selectedUsers.push(user);
|
||||
}
|
||||
} else {
|
||||
if (index > -1) {
|
||||
selectedUsers = [];
|
||||
} else {
|
||||
selectedUsers = [user];
|
||||
}
|
||||
}
|
||||
this.props.onSelectChange(selectedUsers);
|
||||
};
|
||||
|
||||
@@ -229,7 +234,7 @@ class UserSelect extends React.Component {
|
||||
<div className="user-search-container">
|
||||
<SearchInput
|
||||
autoFocus={true}
|
||||
placeholder={this.props.placeholder || gettext('Search users')}
|
||||
placeholder={this.props.searchPlaceholder || gettext('Search users')}
|
||||
value={searchValue}
|
||||
onChange={this.onValueChanged}
|
||||
onKeyDown={this.onKeyDown}
|
||||
@@ -246,6 +251,7 @@ class UserSelect extends React.Component {
|
||||
onClick={this.onUserClick.bind(this, user)}
|
||||
>
|
||||
<UserItem user={user} enableDeleteUser={false} />
|
||||
{selectedUsers.find(u => u.email === user.email) && <i className="sf2-icon-tick"></i>}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
|
@@ -118,6 +118,7 @@
|
||||
|
||||
.tip {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
@@ -41,12 +41,14 @@
|
||||
|
||||
.transfer-dialog-content .transfer-dialog-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-basis: 78%;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.transfer-dialog-content .transfer-dialog-main .tab-content {
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.transfer-dialog-content .transfer-dialog-main .tab-pane {
|
||||
@@ -57,8 +59,7 @@
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.transfer-dialog-content .transfer-dialog-main .user-select,
|
||||
.transfer-dialog-content .transfer-dialog-main .transfer-repo-select-department {
|
||||
.transfer-dialog-content .transfer-dialog-main .user-select {
|
||||
padding: 8px 0;
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
@@ -4,7 +4,7 @@
|
||||
}
|
||||
|
||||
.selected-user-item-container .user-select-placeholder {
|
||||
color: #808080;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.user-select-container {
|
||||
|
Reference in New Issue
Block a user