mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-08 18:30:53 +00:00
[dir view] list view: added support for mobile (#4037)
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { Fragment } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import MD5 from 'MD5';
|
import MD5 from 'MD5';
|
||||||
import { UncontrolledTooltip } from 'reactstrap';
|
import { UncontrolledTooltip } from 'reactstrap';
|
||||||
|
import { Dropdown, DropdownToggle, DropdownItem } from 'reactstrap';
|
||||||
import { gettext, siteRoot, mediaUrl, username } from '../../utils/constants';
|
import { gettext, siteRoot, mediaUrl, username } from '../../utils/constants';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
@@ -70,6 +71,7 @@ class DirentListItem extends React.Component {
|
|||||||
isDropTipshow: false,
|
isDropTipshow: false,
|
||||||
isEditFileTagShow: false,
|
isEditFileTagShow: false,
|
||||||
isPermissionDialogOpen: false,
|
isPermissionDialogOpen: false,
|
||||||
|
isOpMenuOpen: false // for mobile
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +88,12 @@ class DirentListItem extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleOpMenu = () => {
|
||||||
|
this.setState({
|
||||||
|
isOpMenuOpen: !this.state.isOpMenuOpen
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//UI Interactive
|
//UI Interactive
|
||||||
onMouseEnter = () => {
|
onMouseEnter = () => {
|
||||||
if (!this.props.isItemFreezed) {
|
if (!this.props.isItemFreezed) {
|
||||||
@@ -186,6 +194,11 @@ class DirentListItem extends React.Component {
|
|||||||
this.setState({isShareDialogShow: !this.state.isShareDialogShow});
|
this.setState({isShareDialogShow: !this.state.isShareDialogShow});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMobileMenuItemClick = (e) => {
|
||||||
|
const operation = e.target.getAttribute('data-op');
|
||||||
|
this.onMenuItemClick(operation, e);
|
||||||
|
}
|
||||||
|
|
||||||
onMenuItemClick = (operation, event) => {
|
onMenuItemClick = (operation, event) => {
|
||||||
switch(operation) {
|
switch(operation) {
|
||||||
case 'Download':
|
case 'Download':
|
||||||
@@ -586,70 +599,121 @@ class DirentListItem extends React.Component {
|
|||||||
trClass += (activeDirent && activeDirent.name === dirent.name) ? 'tr-active' : '';
|
trClass += (activeDirent && activeDirent.name === dirent.name) ? 'tr-active' : '';
|
||||||
trClass += dirent.isSelected? 'tr-active' : '';
|
trClass += dirent.isSelected? 'tr-active' : '';
|
||||||
|
|
||||||
let lockedInfo = gettext('locked by {name}');
|
let lockedInfo = gettext('locked by {name}').replace('{name}', dirent.lock_owner_name);
|
||||||
lockedInfo = lockedInfo.replace('{name}', dirent.lock_owner_name);
|
|
||||||
|
const isDesktop = Utils.isDesktop();
|
||||||
|
const desktopItem = (
|
||||||
|
<tr
|
||||||
|
className={trClass}
|
||||||
|
draggable="true"
|
||||||
|
onMouseEnter={this.onMouseEnter}
|
||||||
|
onMouseOver={this.onMouseOver}
|
||||||
|
onMouseLeave={this.onMouseLeave}
|
||||||
|
onClick={this.onDirentClick}
|
||||||
|
onDragStart={this.onItemDragStart}
|
||||||
|
onDragEnter={this.onItemDragEnter}
|
||||||
|
onDragOver={this.onItemDragOver}
|
||||||
|
onDragLeave={this.onItemDragLeave}
|
||||||
|
onDrop={this.onItemDragDrop}
|
||||||
|
onMouseDown={this.onItemMouseDown}
|
||||||
|
onContextMenu={this.onItemContextMenu}
|
||||||
|
>
|
||||||
|
<td className={`pl10 ${this.state.isDragTipShow ? 'tr-drag-effect' : ''}`}>
|
||||||
|
<input type="checkbox" className="vam" onChange={this.onItemSelected} checked={dirent.isSelected}/>
|
||||||
|
</td>
|
||||||
|
<td className="pl10">
|
||||||
|
{dirent.starred !== undefined && !dirent.starred && <i className="far fa-star star-empty cursor-pointer" onClick={this.onItemStarred}></i>}
|
||||||
|
{dirent.starred !== undefined && dirent.starred && <i className="fas fa-star cursor-pointer" onClick={this.onItemStarred}></i>}
|
||||||
|
</td>
|
||||||
|
<td className="pl10">
|
||||||
|
<div className="dir-icon">
|
||||||
|
{dirent.encoded_thumbnail_src ?
|
||||||
|
<img ref='drag_icon' src={`${siteRoot}${dirent.encoded_thumbnail_src}`} className="thumbnail cursor-pointer" onClick={this.onItemClick} alt="" /> :
|
||||||
|
<img ref='drag_icon' src={iconUrl} width="24" alt='' />
|
||||||
|
}
|
||||||
|
{dirent.is_locked && <img className="locked" src={mediaUrl + 'img/file-locked-32.png'} alt={gettext('locked')} title={lockedInfo}/>}
|
||||||
|
<div ref="empty_content" style={{position: 'absolute', width: '1px', height: '1px'}}></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="name">
|
||||||
|
{this.state.isRenameing ?
|
||||||
|
<Rename hasSuffix={dirent.type !== 'dir'} name={dirent.name} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel} /> :
|
||||||
|
<a href={dirent.type === 'dir' ? dirHref : fileHref} onClick={this.onItemClick}>{dirent.name}</a>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td className="tag-list-title">
|
||||||
|
{(dirent.type !== 'dir' && dirent.file_tags && dirent.file_tags.length > 0) && (
|
||||||
|
<Fragment>
|
||||||
|
<div id={`tag-list-title-${toolTipID}`} className="dirent-item tag-list tag-list-stacked">
|
||||||
|
{dirent.file_tags.map((fileTag, index) => {
|
||||||
|
let length = dirent.file_tags.length;
|
||||||
|
return (
|
||||||
|
<span className="file-tag" key={fileTag.id} style={{zIndex:length - index, backgroundColor:fileTag.color}}></span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<UncontrolledTooltip target={`tag-list-title-${toolTipID}`} placement="bottom">
|
||||||
|
{tagTitle}
|
||||||
|
</UncontrolledTooltip>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td className="operation">{this.renderItemOperation()}</td>
|
||||||
|
<td className="file-size">{dirent.size && dirent.size}</td>
|
||||||
|
<td className="last-update">{dirent.mtime_relative}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
const mobileItem = (
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div className="dir-icon">
|
||||||
|
{dirent.encoded_thumbnail_src ?
|
||||||
|
<img src={`${siteRoot}${dirent.encoded_thumbnail_src}`} className="thumbnail cursor-pointer" onClick={this.onItemClick} alt="" /> :
|
||||||
|
<img src={iconUrl} width="24" alt="" />
|
||||||
|
}
|
||||||
|
{dirent.is_locked && <img className="locked" src={mediaUrl + 'img/file-locked-32.png'} alt={gettext('locked')} title={lockedInfo}/>}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{this.state.isRenameing ?
|
||||||
|
<Rename hasSuffix={dirent.type !== 'dir'} name={dirent.name} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel} /> :
|
||||||
|
<a href={dirent.type === 'dir' ? dirHref : fileHref} onClick={this.onItemClick}>{dirent.name}</a>
|
||||||
|
}
|
||||||
|
<br />
|
||||||
|
{dirent.size && <span className="item-meta-info">{dirent.size}</span>}
|
||||||
|
<span className="item-meta-info">{dirent.mtime_relative}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Dropdown isOpen={this.state.isOpMenuOpen} toggle={this.toggleOpMenu}>
|
||||||
|
<DropdownToggle
|
||||||
|
tag="i"
|
||||||
|
className="sf-dropdown-toggle fa fa-ellipsis-v ml-0"
|
||||||
|
title={gettext('More Operations')}
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-expanded={this.state.isOpMenuOpen}
|
||||||
|
/>
|
||||||
|
<div className={this.state.isOpMenuOpen ? '' : 'd-none'} onClick={this.toggleOpMenu}>
|
||||||
|
<div className="mobile-operation-menu-bg-layer"></div>
|
||||||
|
<div className="mobile-operation-menu">
|
||||||
|
{dirent.starred !== undefined &&
|
||||||
|
<DropdownItem className="mobile-menu-item" onClick={this.onItemStarred}>{dirent.starred ? gettext('Unstar') : gettext('Star')}</DropdownItem>}
|
||||||
|
{this.props.getDirentItemMenuList(dirent, true).map((item, index) => {
|
||||||
|
if (item != 'Divider' && item.key != 'Open via Client') {
|
||||||
|
return (
|
||||||
|
<DropdownItem className="mobile-menu-item" key={index} data-op={item.key} onClick={this.onMobileMenuItemClick}>{item.value}</DropdownItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dropdown>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<tr
|
{isDesktop ? desktopItem : mobileItem}
|
||||||
className={trClass}
|
|
||||||
draggable="true"
|
|
||||||
onMouseEnter={this.onMouseEnter}
|
|
||||||
onMouseOver={this.onMouseOver}
|
|
||||||
onMouseLeave={this.onMouseLeave}
|
|
||||||
onClick={this.onDirentClick}
|
|
||||||
onDragStart={this.onItemDragStart}
|
|
||||||
onDragEnter={this.onItemDragEnter}
|
|
||||||
onDragOver={this.onItemDragOver}
|
|
||||||
onDragLeave={this.onItemDragLeave}
|
|
||||||
onDrop={this.onItemDragDrop}
|
|
||||||
onMouseDown={this.onItemMouseDown}
|
|
||||||
onContextMenu={this.onItemContextMenu}
|
|
||||||
>
|
|
||||||
<td className={`pl10 ${this.state.isDragTipShow ? 'tr-drag-effect' : ''}`}>
|
|
||||||
<input type="checkbox" className="vam" onChange={this.onItemSelected} checked={dirent.isSelected}/>
|
|
||||||
</td>
|
|
||||||
<td className="pl10">
|
|
||||||
{dirent.starred !== undefined && !dirent.starred && <i className="far fa-star star-empty cursor-pointer" onClick={this.onItemStarred}></i>}
|
|
||||||
{dirent.starred !== undefined && dirent.starred && <i className="fas fa-star cursor-pointer" onClick={this.onItemStarred}></i>}
|
|
||||||
</td>
|
|
||||||
<td className="pl10">
|
|
||||||
<div className="dir-icon">
|
|
||||||
{dirent.encoded_thumbnail_src ?
|
|
||||||
<img ref='drag_icon' src={`${siteRoot}${dirent.encoded_thumbnail_src}`} className="thumbnail cursor-pointer" onClick={this.onItemClick} alt="" /> :
|
|
||||||
<img ref='drag_icon' src={iconUrl} width="24" alt='' />
|
|
||||||
}
|
|
||||||
{dirent.is_locked && <img className="locked" src={mediaUrl + 'img/file-locked-32.png'} alt={gettext('locked')} title={lockedInfo}/>}
|
|
||||||
<div ref="empty_content" style={{position: 'absolute', width: '1px', height: '1px'}}></div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="name">
|
|
||||||
{this.state.isRenameing ?
|
|
||||||
<Rename hasSuffix={dirent.type !== 'dir'} name={dirent.name} onRenameConfirm={this.onRenameConfirm} onRenameCancel={this.onRenameCancel} /> :
|
|
||||||
<a href={dirent.type === 'dir' ? dirHref : fileHref} onClick={this.onItemClick}>{dirent.name}</a>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td className="tag-list-title">
|
|
||||||
{(dirent.type !== 'dir' && dirent.file_tags && dirent.file_tags.length > 0) && (
|
|
||||||
<Fragment>
|
|
||||||
<div id={`tag-list-title-${toolTipID}`} className="dirent-item tag-list tag-list-stacked">
|
|
||||||
{dirent.file_tags.map((fileTag, index) => {
|
|
||||||
let length = dirent.file_tags.length;
|
|
||||||
return (
|
|
||||||
<span className="file-tag" key={fileTag.id} style={{zIndex:length - index, backgroundColor:fileTag.color}}></span>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<UncontrolledTooltip target={`tag-list-title-${toolTipID}`} placement="bottom">
|
|
||||||
{tagTitle}
|
|
||||||
</UncontrolledTooltip>
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className="operation">{this.renderItemOperation()}</td>
|
|
||||||
<td className="file-size">{dirent.size && dirent.size}</td>
|
|
||||||
<td className="last-update">{dirent.mtime_relative}</td>
|
|
||||||
</tr>
|
|
||||||
{this.state.isMoveDialogShow &&
|
{this.state.isMoveDialogShow &&
|
||||||
<ModalPortal>
|
<ModalPortal>
|
||||||
<MoveDirentDialog
|
<MoveDirentDialog
|
||||||
|
@@ -652,6 +652,8 @@ class DirentListView extends React.Component {
|
|||||||
const sortBySize = sortBy == 'size';
|
const sortBySize = sortBy == 'size';
|
||||||
const sortIcon = sortOrder == 'asc' ? <span className="fas fa-caret-up"></span> : <span className="fas fa-caret-down"></span>;
|
const sortIcon = sortOrder == 'asc' ? <span className="fas fa-caret-up"></span> : <span className="fas fa-caret-down"></span>;
|
||||||
|
|
||||||
|
const isDesktop = Utils.isDesktop();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`table-container ${this.state.isListDropTipShow ? 'table-drop-active' : ''}`}
|
className={`table-container ${this.state.isListDropTipShow ? 'table-drop-active' : ''}`}
|
||||||
@@ -663,7 +665,8 @@ class DirentListView extends React.Component {
|
|||||||
onDragLeave={this.onTableDragLeave}
|
onDragLeave={this.onTableDragLeave}
|
||||||
onDrop={this.tableDrop}
|
onDrop={this.tableDrop}
|
||||||
>
|
>
|
||||||
<table>
|
<table className={`table-hover ${isDesktop ? '': 'table-thead-hidden'}`}>
|
||||||
|
{isDesktop ? (
|
||||||
<thead onMouseDown={this.onThreadMouseDown} onContextMenu={this.onThreadContextMenu}>
|
<thead onMouseDown={this.onThreadMouseDown} onContextMenu={this.onThreadContextMenu}>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="3%" className="pl10">
|
<th width="3%" className="pl10">
|
||||||
@@ -678,6 +681,15 @@ class DirentListView extends React.Component {
|
|||||||
<th width="15%"><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortByTime && sortIcon}</a></th>
|
<th width="15%"><a className="d-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Last Update')} {sortByTime && sortIcon}</a></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
) : (
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="12%"></th>
|
||||||
|
<th width="80%"></th>
|
||||||
|
<th width="8%"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
)}
|
||||||
<tbody>
|
<tbody>
|
||||||
{direntList.map((dirent, index) => {
|
{direntList.map((dirent, index) => {
|
||||||
return (
|
return (
|
||||||
|
Reference in New Issue
Block a user