1
0
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:
Michael An
2025-05-09 17:28:05 +08:00
committed by GitHub
parent 77260ba050
commit df4ba2a8c2
10 changed files with 130 additions and 79 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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>
);
});

View File

@@ -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>
);
}

View File

@@ -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;
}

View File

@@ -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>
);
})

View File

@@ -118,6 +118,7 @@
.tip {
color: #666;
font-size: 13px;
margin-bottom: 1rem;
}

View File

@@ -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;

View File

@@ -4,7 +4,7 @@
}
.selected-user-item-container .user-select-placeholder {
color: #808080;
color: #999;
}
.user-select-container {