mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-14 22:33:17 +00:00
Fix mobile UI in wiki trash dialog and share dialog (#7612)
* 01 change mobile wiki trash dialog * 02 share dialog support mobile use * 03 fix share to group click outside
This commit is contained in:
@@ -2,7 +2,6 @@ import React, { Component } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import SearchInput from '../../search-input';
|
import SearchInput from '../../search-input';
|
||||||
import ClickOutside from '../../click-outside';
|
|
||||||
import Option from './option';
|
import Option from './option';
|
||||||
import KeyCodes from '../../../constants/keyCodes';
|
import KeyCodes from '../../../constants/keyCodes';
|
||||||
|
|
||||||
@@ -26,6 +25,7 @@ class SelectOptionGroup extends Component {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
window.addEventListener('keydown', this.onHotKey);
|
window.addEventListener('keydown', this.onHotKey);
|
||||||
|
document.addEventListener('mousedown', this.handleDocumentClick);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.resetMenuStyle();
|
this.resetMenuStyle();
|
||||||
}, 1);
|
}, 1);
|
||||||
@@ -35,8 +35,13 @@ class SelectOptionGroup extends Component {
|
|||||||
this.filterOptions = null;
|
this.filterOptions = null;
|
||||||
this.timer && clearTimeout(this.timer);
|
this.timer && clearTimeout(this.timer);
|
||||||
window.removeEventListener('keydown', this.onHotKey);
|
window.removeEventListener('keydown', this.onHotKey);
|
||||||
|
document.removeEventListener('mousedown', this.handleDocumentClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDocumentClick = (e) => {
|
||||||
|
this.props.onClickOutside(e);
|
||||||
|
};
|
||||||
|
|
||||||
resetMenuStyle = () => {
|
resetMenuStyle = () => {
|
||||||
const { isInModal, position } = this.props;
|
const { isInModal, position } = this.props;
|
||||||
const { top, height } = this.optionGroupRef.getBoundingClientRect();
|
const { top, height } = this.optionGroupRef.getBoundingClientRect();
|
||||||
@@ -170,27 +175,25 @@ class SelectOptionGroup extends Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ClickOutside onClickOutside={this.props.onClickOutside}>
|
<div
|
||||||
<div
|
className={classnames('pt-0 option-group', className ? 'option-group-' + className : '')}
|
||||||
className={classnames('pt-0 option-group', className ? 'option-group-' + className : '')}
|
ref={(ref) => this.optionGroupRef = ref}
|
||||||
ref={(ref) => this.optionGroupRef = ref}
|
style={style}
|
||||||
style={style}
|
onMouseDown={this.onMouseDown}
|
||||||
onMouseDown={this.onMouseDown}
|
>
|
||||||
>
|
<div className="option-group-search position-relative">
|
||||||
<div className="option-group-search position-relative">
|
<SearchInput
|
||||||
<SearchInput
|
className="option-search-control"
|
||||||
className="option-search-control"
|
autoFocus={isInModal}
|
||||||
autoFocus={isInModal}
|
placeholder={searchPlaceholder}
|
||||||
placeholder={searchPlaceholder}
|
onChange={this.onChangeSearch}
|
||||||
onChange={this.onChangeSearch}
|
ref={this.searchInputRef}
|
||||||
ref={this.searchInputRef}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="option-group-content" ref={(ref) => this.optionGroupContentRef = ref}>
|
|
||||||
{this.renderOptGroup(searchVal)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</ClickOutside>
|
<div className="option-group-content" ref={(ref) => this.optionGroupContentRef = ref}>
|
||||||
|
{this.renderOptGroup(searchVal)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import { Button } from 'reactstrap';
|
import { Button } from 'reactstrap';
|
||||||
import { gettext, isPro, enableShareToDepartment } from '../../utils/constants';
|
import { gettext, isPro, enableShareToDepartment } from '../../utils/constants';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils, isMobile } from '../../utils/utils';
|
||||||
import toaster from '../toast';
|
import toaster from '../toast';
|
||||||
import SharePermissionEditor from '../select-editor/share-permission-editor';
|
import SharePermissionEditor from '../select-editor/share-permission-editor';
|
||||||
import EventBus from '../common/event-bus';
|
import EventBus from '../common/event-bus';
|
||||||
@@ -40,6 +40,36 @@ class GroupItem extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
let item = this.props.item;
|
let item = this.props.item;
|
||||||
let currentPermission = Utils.getSharedPermission(item);
|
let currentPermission = Utils.getSharedPermission(item);
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td className='name'>{item.group_info.name}</td>
|
||||||
|
<td>
|
||||||
|
<SharePermissionEditor
|
||||||
|
repoID={this.props.repoID}
|
||||||
|
isTextMode={true}
|
||||||
|
autoFocus={true}
|
||||||
|
isEditIconShow={this.state.isOperationShow}
|
||||||
|
currentPermission={currentPermission}
|
||||||
|
permissions={this.props.permissions}
|
||||||
|
onPermissionChanged={this.onChangeUserPermission}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
tabIndex="0"
|
||||||
|
role="button"
|
||||||
|
className='sf2-icon-x3 action-icon'
|
||||||
|
onClick={this.deleteShareItem}
|
||||||
|
onKeyDown={Utils.onKeyDown}
|
||||||
|
title={gettext('Delete')}
|
||||||
|
aria-label={gettext('Delete')}
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<tr onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} tabIndex="0" onFocus={this.onMouseEnter}>
|
<tr onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} tabIndex="0" onFocus={this.onMouseEnter}>
|
||||||
<td className='name'>{item.group_info.name}</td>
|
<td className='name'>{item.group_info.name}</td>
|
||||||
@@ -352,7 +382,7 @@ class ShareToGroup extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const thead = (
|
let thead = (
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="47%">{gettext('Group')}</th>
|
<th width="47%">{gettext('Group')}</th>
|
||||||
@@ -361,9 +391,20 @@ class ShareToGroup extends React.Component {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
);
|
);
|
||||||
|
if (isMobile) {
|
||||||
|
thead = (
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="43%">{gettext('Group')}</th>
|
||||||
|
<th width="35%">{gettext('Permission')}</th>
|
||||||
|
<th width="22%"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<table className="w-xs-200">
|
<table>
|
||||||
{thead}
|
{thead}
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -392,7 +433,7 @@ class ShareToGroup extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<Button color="primary" onClick={this.shareToGroup}>{gettext('Submit')}</Button>
|
<Button color="primary" onClick={this.shareToGroup} size={isMobile ? 'sm' : 'md'}>{gettext('Submit')}</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{this.state.errorMsg.length > 0 &&
|
{this.state.errorMsg.length > 0 &&
|
||||||
@@ -408,7 +449,7 @@ class ShareToGroup extends React.Component {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div className="share-list-container">
|
<div className="share-list-container">
|
||||||
<table className="table-thead-hidden w-xs-200">
|
<table className="table-thead-hidden">
|
||||||
{thead}
|
{thead}
|
||||||
<GroupList
|
<GroupList
|
||||||
repoID={this.props.repoID}
|
repoID={this.props.repoID}
|
||||||
|
@@ -4,7 +4,7 @@ import classnames from 'classnames';
|
|||||||
import { gettext, isPro, cloudMode, isOrgContext } from '../../utils/constants';
|
import { gettext, isPro, cloudMode, isOrgContext } from '../../utils/constants';
|
||||||
import { Button } from 'reactstrap';
|
import { Button } from 'reactstrap';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils, isMobile } from '../../utils/utils';
|
||||||
import toaster from '../toast';
|
import toaster from '../toast';
|
||||||
import UserSelect from '../user-select';
|
import UserSelect from '../user-select';
|
||||||
import SharePermissionEditor from '../select-editor/share-permission-editor';
|
import SharePermissionEditor from '../select-editor/share-permission-editor';
|
||||||
@@ -52,6 +52,65 @@ class UserItem extends React.Component {
|
|||||||
let item = this.props.item;
|
let item = this.props.item;
|
||||||
let currentPermission = Utils.getSharedPermission(item);
|
let currentPermission = Utils.getSharedPermission(item);
|
||||||
const { isUserDetailsPopoverOpen } = this.state;
|
const { isUserDetailsPopoverOpen } = this.state;
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td className="name">
|
||||||
|
<div className="position-relative d-flex align-items-center">
|
||||||
|
<img
|
||||||
|
src={item.user_info.avatar_url}
|
||||||
|
width="24"
|
||||||
|
alt={item.user_info.nickname}
|
||||||
|
className="rounded-circle mr-2 cursor-pointer"
|
||||||
|
onMouseEnter={this.userAvatarOnMouseEnter}
|
||||||
|
onMouseLeave={this.userAvatarOnMouseLeave}
|
||||||
|
/>
|
||||||
|
<span>{item.user_info.nickname}</span>
|
||||||
|
{isUserDetailsPopoverOpen && (
|
||||||
|
<div className="user-details-popover p-4 position-absolute w-100 mt-1">
|
||||||
|
<div className="user-details-main pb-3">
|
||||||
|
<img
|
||||||
|
src={item.user_info.avatar_url}
|
||||||
|
width="40"
|
||||||
|
alt={item.user_info.nickname}
|
||||||
|
className="rounded-circle mr-2"
|
||||||
|
/>
|
||||||
|
<span className="user-details-name">{item.user_info.nickname}</span>
|
||||||
|
</div>
|
||||||
|
<dl className="m-0 mt-3 d-flex">
|
||||||
|
<dt className="m-0 mr-3">{gettext('Email')}</dt>
|
||||||
|
<dd className="m-0">{item.user_info.contact_email}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<SharePermissionEditor
|
||||||
|
repoID={this.props.repoID}
|
||||||
|
isTextMode={true}
|
||||||
|
autoFocus={true}
|
||||||
|
isEditIconShow={true}
|
||||||
|
currentPermission={currentPermission}
|
||||||
|
permissions={this.props.permissions}
|
||||||
|
onPermissionChanged={this.onChangeUserPermission}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
tabIndex="0"
|
||||||
|
role="button"
|
||||||
|
className='sf2-icon-x3 action-icon'
|
||||||
|
onClick={this.deleteShareItem}
|
||||||
|
onKeyDown={Utils.onKeyDown}
|
||||||
|
title={gettext('Delete')}
|
||||||
|
aria-label={gettext('Delete')}
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<tr onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} tabIndex="0" onFocus={this.onMouseEnter}>
|
<tr onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} tabIndex="0" onFocus={this.onMouseEnter}>
|
||||||
<td className="name">
|
<td className="name">
|
||||||
@@ -438,7 +497,7 @@ class ShareToUser extends React.Component {
|
|||||||
showDeptBtn = false;
|
showDeptBtn = false;
|
||||||
}
|
}
|
||||||
let { sharedItems } = this.state;
|
let { sharedItems } = this.state;
|
||||||
const thead = (
|
let thead = (
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="47%">{gettext('User')}</th>
|
<th width="47%">{gettext('User')}</th>
|
||||||
@@ -447,9 +506,20 @@ class ShareToUser extends React.Component {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
);
|
);
|
||||||
|
if (isMobile) {
|
||||||
|
thead = (
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="43%">{gettext('User')}</th>
|
||||||
|
<th width="35%">{gettext('Permission')}</th>
|
||||||
|
<th width="22%"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="share-link-container">
|
<div className="share-link-container">
|
||||||
<table className="w-xs-200">
|
<table>
|
||||||
{thead}
|
{thead}
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -485,7 +555,7 @@ class ShareToUser extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<Button color="primary" onClick={this.shareToUser}>{gettext('Submit')}</Button>
|
<Button color="primary" onClick={this.shareToUser} size={isMobile ? 'sm' : 'md'}>{gettext('Submit')}</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{this.state.errorMsg.length > 0 &&
|
{this.state.errorMsg.length > 0 &&
|
||||||
@@ -506,7 +576,7 @@ class ShareToUser extends React.Component {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div className="share-list-container">
|
<div className="share-list-container">
|
||||||
<table className="table-thead-hidden w-xs-200">
|
<table className="table-thead-hidden">
|
||||||
{thead}
|
{thead}
|
||||||
<UserList
|
<UserList
|
||||||
repoID={this.props.repoID}
|
repoID={this.props.repoID}
|
||||||
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Modal, ModalBody, ModalHeader } from 'reactstrap';
|
import { Modal, ModalBody, ModalHeader } from 'reactstrap';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils, isMobile } from '../../utils/utils';
|
||||||
import { gettext, wikiId } from '../../utils/constants';
|
import { gettext, wikiId } from '../../utils/constants';
|
||||||
import wikiAPI from '../../utils/wiki-api';
|
import wikiAPI from '../../utils/wiki-api';
|
||||||
import ModalPortal from '../../components/modal-portal';
|
import ModalPortal from '../../components/modal-portal';
|
||||||
@@ -137,13 +137,22 @@ class Content extends React.Component {
|
|||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.theadData = [
|
if (isMobile) {
|
||||||
{ width: '5%', text: gettext('Name') },
|
this.theadData = [
|
||||||
{ width: '20%', text: '' },
|
{ width: '30%', text: gettext('Name') },
|
||||||
{ width: '30%', text: gettext('Size') },
|
{ width: '20%', text: gettext('Size') },
|
||||||
{ width: '35%', text: gettext('Delete Time') },
|
{ width: '30%', text: gettext('Delete Time') },
|
||||||
{ width: '10%', text: '' }
|
{ width: '20%', text: '' }
|
||||||
];
|
];
|
||||||
|
} else {
|
||||||
|
this.theadData = [
|
||||||
|
{ width: '5%', text: gettext('Name') },
|
||||||
|
{ width: '20%', text: '' },
|
||||||
|
{ width: '30%', text: gettext('Size') },
|
||||||
|
{ width: '35%', text: gettext('Delete Time') },
|
||||||
|
{ width: '10%', text: '' }
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPreviousPage = () => {
|
getPreviousPage = () => {
|
||||||
@@ -251,6 +260,16 @@ class Item extends React.Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { isAdmin } = window.wiki.config;
|
const { isAdmin } = window.wiki.config;
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td>{item.name}</td>
|
||||||
|
<td>{Utils.bytesToSize(item.size)}</td>
|
||||||
|
<td title={dayjs(item.deleted_time).format('dddd, MMMM D, YYYY h:mm:ss A')}>{dayjs(item.deleted_time).format('YYYY-MM-DD')}</td>
|
||||||
|
<td>{isAdmin && <a href="#" onClick={this.restoreItem} role="button">{gettext('Restore')}</a>}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} onFocus={this.handleMouseOver}>
|
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} onFocus={this.handleMouseOver}>
|
||||||
<td><NavItemIcon symbol={'file'} disable={true} /></td>
|
<td><NavItemIcon symbol={'file'} disable={true} /></td>
|
||||||
|
Reference in New Issue
Block a user