1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-02 15:38:15 +00:00

Grid view mode four edition

This commit is contained in:
zxj96
2019-04-18 22:36:07 +08:00
parent 70eb5ca291
commit 9965735be3
7 changed files with 957 additions and 4 deletions

View File

@@ -0,0 +1,125 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import { Button, Modal, ModalHeader, Input, ModalBody, ModalFooter, Alert } from 'reactstrap';
const propTypes = {
onRename: PropTypes.func.isRequired,
toggleCancel: PropTypes.func.isRequired,
checkDuplicatedName: PropTypes.func.isRequired,
dirent: PropTypes.object,
};
class Rename extends React.Component {
constructor(props) {
super(props);
this.state = {
newName: '',
errMessage: '',
};
this.newInput = React.createRef();
}
componentWillMount() {
this.setState({newName: this.props.dirent.name});
}
componentDidMount() {
let {dirent} = this.props;
this.changeState(dirent);
this.newInput.focus();
let type = dirent.type;
if (type === 'file') {
var endIndex = dirent.name.lastIndexOf('.md');
this.newInput.setSelectionRange(0, endIndex, 'forward');
} else {
this.newInput.setSelectionRange(0, -1);
}
}
componentWillReceiveProps(nextProps) {
this.changeState(nextProps.dirent);
}
handleChange = (e) => {
this.setState({newName: e.target.value});
}
handleSubmit = () => {
let { isValid, errMessage } = this.validateInput();
if (!isValid) {
this.setState({errMessage : errMessage});
} else {
let isDuplicated = this.checkDuplicatedName();
if (isDuplicated) {
let errMessage = gettext('The name "{name}" is already taken. Please choose a different name.');
errMessage = errMessage.replace('{name}', Utils.HTMLescape(this.state.newName));
this.setState({errMessage: errMessage});
} else {
this.props.onRename(this.state.newName);
}
}
}
handleKeyPress = (e) => {
if (e.key === 'Enter') {
this.handleSubmit();
}
}
toggle = () => {
this.props.toggleCancel();
}
changeState = (dirent) => {
let name = dirent.name;
this.setState({newName: name});
}
validateInput = () => {
let newName = this.state.newName.trim();
let isValid = true;
let errMessage = '';
if (!newName) {
isValid = false;
errMessage = gettext('Name is required.');
return { isValid, errMessage };
}
if (newName.indexOf('/') > -1) {
isValid = false;
errMessage = gettext('Name should not include ' + '\'/\'' + '.');
return { isValid, errMessage };
}
return { isValid, errMessage };
}
checkDuplicatedName = () => {
let isDuplicated = this.props.checkDuplicatedName(this.state.newName);
return isDuplicated;
}
render() {
let type = this.props.dirent.type;
return (
<Modal isOpen={true} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>{type === 'file' ? gettext('Rename File') : gettext('Rename Folder') }</ModalHeader>
<ModalBody>
<p>{type === 'file' ? gettext('New file name'): gettext('New folder name')}</p>
<Input onKeyPress={this.handleKeyPress} innerRef={input => {this.newInput = input;}} placeholder="newName" value={this.state.newName} onChange={this.handleChange} />
{this.state.errMessage && <Alert color="danger" className="mt-2">{this.state.errMessage}</Alert>}
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={this.toggle}>{gettext('Cancel')}</Button>
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
</ModalFooter>
</Modal>
);
}
}
Rename.propTypes = propTypes;
export default Rename;

View File

@@ -1,14 +1,81 @@
import React from 'react';
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import RepoInfoBar from '../../components/repo-info-bar';
import DirentGridView from '../../components/dirent-grid-view/dirent-grid-view';
import DirentNoneView from '../../components/dirent-list-view/dirent-none-view';
const propTypes = {
path: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired,
currentRepoInfo: PropTypes.object.isRequired,
readmeMarkdown: PropTypes.object,
draftCounts: PropTypes.number,
usedRepoTags: PropTypes.array.isRequired,
updateUsedRepoTags: PropTypes.func.isRequired,
direntList: PropTypes.array.isRequired,
onItemClick: PropTypes.func.isRequired,
onGridItemClick: PropTypes.func,
onAddFile: PropTypes.func.isRequired,
onItemDelete: PropTypes.func.isRequired,
onItemMove: PropTypes.func.isRequired,
onItemCopy: PropTypes.func.isRequired,
onRenameNode: PropTypes.func.isRequired,
isGroupOwnedRepo: PropTypes.bool.isRequired,
isRepoInfoBarShow: PropTypes.bool.isRequired,
isDirentListLoading: PropTypes.bool.isRequired,
isDirentDetailShow: PropTypes.bool.isRequired,
enableDirPrivateShare: PropTypes.bool.isRequired,
updateDirent: PropTypes.func.isRequired,
showShareBtn: PropTypes.bool.isRequired,
showDirentDetail: PropTypes.func.isRequired,
};
class DirGridView extends React.Component {
render() {
if (this.props.path === '/' && this.props.direntList.length === 0) {
return (
<div>表格布局</div>
<DirentNoneView
path={this.props.path}
isDirentListLoading={this.props.isDirentListLoading}
onAddFile={this.props.onAddFile}
/>
);
}
return (
<Fragment>
{this.props.isRepoInfoBarShow && (
<RepoInfoBar
repoID={this.props.repoID}
currentPath={this.props.path}
readmeMarkdown={this.props.readmeMarkdown}
draftCounts={this.props.draftCounts}
usedRepoTags={this.props.usedRepoTags}
updateUsedRepoTags={this.props.updateUsedRepoTags}
/>
)}
<DirentGridView
path={this.props.path}
repoID={this.props.repoID}
currentRepoInfo={this.props.currentRepoInfo}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
enableDirPrivateShare={this.props.enableDirPrivateShare}
direntList={this.props.direntList}
onAddFile={this.props.onAddFile}
onItemClick={this.props.onItemClick}
onItemDelete={this.props.onItemDelete}
onItemMove={this.props.onItemMove}
onItemCopy={this.props.onItemCopy}
isDirentListLoading={this.props.isDirentListLoading}
updateDirent={this.props.updateDirent}
showShareBtn={this.props.showShareBtn}
onRenameNode={this.props.onRenameNode}
showDirentDetail={this.props.showDirentDetail}
onGridItemClick={this.props.onGridItemClick}
isDirentDetailShow={this.props.isDirentDetailShow}
onItemRename={this.props.onItemRename}
/>
</Fragment>
);
}
}

View File

@@ -0,0 +1,119 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { gettext, siteRoot, mediaUrl } from '../../utils/constants';
import { Utils } from '../../utils/utils';
const propTypes = {
path: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired,
dirent: PropTypes.object.isRequired,
onItemClick: PropTypes.func.isRequired,
showImagePopup: PropTypes.func.isRequired,
handleContextClick: PropTypes.func.isRequired,
};
class DirentGridItem extends React.Component {
constructor(props) {
super(props);
this.state = {
gridDragImage: '',
isDropTipshow: false,
}
}
onItemClick = (e) => {
e.preventDefault();
const dirent = this.props.dirent;
if (Utils.imageCheck(dirent.name)) {
this.props.showImagePopup(dirent);
} else {
this.props.onItemClick(dirent);
}
}
getFileUrl = (url) => {
let fileUrlArr = url.split('/');
if (fileUrlArr.indexOf('48') !== -1) {
fileUrlArr.splice(fileUrlArr.indexOf('48'), 1, '192');
}
let fileUrl = fileUrlArr.join('/');
return fileUrl;
}
gridDargStart = (e) => {
// todo
}
gridDragEnter = (e) => {
// todo
console.log(123)
}
gridDragOver = (e) => {
// todo
e.preventDefault();
}
gridDragLeave = (e) => {
// todo
console.log(456)
}
gridDrop = (e) => {
// todo
e.preventDefault();
}
onItemContextMenu = (event) => {
this.handleContextClick(event);
}
handleContextClick = (event) => {
this.props.handleContextClick(event, this.props.dirent);
}
render() {
let { dirent, path } = this.props;
let direntPath = Utils.joinPath(path, dirent.name);
let iconUrl = Utils.getDirentIcon(dirent);
let fileUrl = dirent.encoded_thumbnail_src ? this.getFileUrl(dirent.encoded_thumbnail_src) : '';
let dirHref = '';
if (this.props.currentRepoInfo) {
dirHref = siteRoot + 'library/' + this.props.repoID + '/' + this.props.currentRepoInfo.repo_name + Utils.encodePath(direntPath);
}
let fileHref = siteRoot + 'lib/' + this.props.repoID + '/file' + Utils.encodePath(direntPath);
return(
<Fragment>
<li className="grid-item" onContextMenu={this.onItemContextMenu}>
<div
className="grid-file-img-link cursor-pointer"
// draggable="true"
onClick={this.onItemClick}
// onDragStart={this.gridDargStart}
onDragEnter={this.gridDragEnter}
onDragOver={this.gridDragOver}
onDragLeave={this.gridDragLeave}
onDrop={this.gridDrop}
>
{dirent.encoded_thumbnail_src ?
<img src={`${siteRoot}${fileUrl}`} ref={this.gridIcon} className="thumbnail" onClick={this.onItemClick} alt=""/> :
<img src={iconUrl} ref={this.gridIcon} width="96" alt='' />
}
{dirent.is_locked && <img className="grid-file-locked-icon" src={mediaUrl + 'img/file-locked-32.png'} alt={gettext('locked')} title={dirent.lock_owner_name}/>}
</div>
<div className="grid-file-name">
<a className="grid-file-name-link" href={dirent.type === 'dir' ? dirHref : fileHref} onClick={this.onItemClick}>{dirent.name}</a>
</div>
</li>
</Fragment>
)
}
}
DirentGridItem.propTypes = propTypes;
export default DirentGridItem;

View File

@@ -0,0 +1,538 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { siteRoot, thumbnailSizeForOriginal, username, isPro, enableFileComment, fileAuditEnabled, folderPermEnabled } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import { seafileAPI } from '../../utils/seafile-api';
import URLDecorator from '../../utils/url-decorator';
import Loading from '../loading';
import ModalPortal from '../modal-portal';
import ImageDialog from '../../components/dialog/image-dialog';
import DirentGridItem from '../../components/dirent-grid-view/dirent-grid-item';
import ContextMenu from '../context-menu/context-menu';
import { hideMenu, showMenu } from '../context-menu/actions';
import TextTranslation from '../../utils/text-translation';
import MoveDirentDialog from '../dialog/move-dirent-dialog';
import CopyDirentDialog from '../dialog/copy-dirent-dialog';
import ShareDialog from '../dialog/share-dialog';
import ZipDownloadDialog from '../dialog/zip-download-dialog';
import Rename from '../../components/dialog/rename-grid-item-dialog';
import '../../css/grid-view.css';
const propTypes = {
path: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired,
currentRepoInfo: PropTypes.object,
direntList: PropTypes.array.isRequired,
onAddFile: PropTypes.func,
onItemDelete: PropTypes.func,
onItemCopy: PropTypes.func.isRequired,
onItemMove: PropTypes.func.isRequired,
onRenameNode: PropTypes.func.isRequired,
onItemClick: PropTypes.func.isRequired,
isDirentListLoading: PropTypes.bool.isRequired,
isGroupOwnedRepo: PropTypes.bool.isRequired,
showShareBtn: PropTypes.bool.isRequired,
enableDirPrivateShare: PropTypes.bool.isRequired,
updateDirent: PropTypes.func.isRequired,
isDirentDetailShow: PropTypes.bool.isRequired,
onGridItemClick: PropTypes.func,
};
class DirentGridView extends React.Component{
constructor(props) {
super(props);
this.state={
isImagePopupOpen: false,
imageItems: [],
imageIndex: 0,
isCreateFileDialogShow: false,
// onmenuClick
isShareDialogShow: false,
isMoveDialogShow: false,
isCopyDialogShow: false,
isZipDialogOpen: false,
isRenameDialogShow: false,
isMutipleOperation: false,
dirent: '',
}
this.isRepoOwner = props.currentRepoInfo.owner_email === username;
}
onCreateFileToggle = () => {
this.setState({
isCreateFileDialogShow: !this.state.isCreateFileDialogShow,
fileType: ''
});
}
onCreateNewFile = (suffix) => {
this.setState({
isCreateFileDialogShow: !this.state.isCreateFileDialogShow,
fileType: suffix
});
}
onMoveToggle = () => {
this.setState({isMoveDialogShow: !this.state.isMoveDialogShow});
}
onCopyToggle = () => {
this.setState({isCopyDialogShow: !this.state.isCopyDialogShow});
}
onAddFile = (filePath, isDraft) => {
this.setState({isCreateFileDialogShow: false});
this.props.onAddFile(filePath, isDraft);
}
onItemShare = (e) => {
e.nativeEvent.stopImmediatePropagation(); //for document event
this.setState({isShareDialogShow: !this.state.isShareDialogShow});
}
closeSharedDialog = () => {
this.setState({isShareDialogShow: !this.state.isShareDialogShow});
}
onItemDelete = (currentObject, e) => {
e.nativeEvent.stopImmediatePropagation(); //for document event
this.props.onItemDelete(currentObject);
}
onMenuItemClick = (operation, currentObject, event) => {
hideMenu();
switch(operation) {
case 'Download':
this.onItemDownload(currentObject, event);
break;
case 'Share':
this.onItemShare(event);
break;
case 'Delete':
this.onItemDelete(currentObject, event);
break;
case 'Rename':
this.onItemRenameToggle();
break;
case 'Move':
this.onItemMoveToggle();
break;
case 'Copy':
this.onItemCopyToggle();
break;
case 'Permission':
this.onPermissionItem();
break;
case 'Unlock':
this.onUnlockItem(currentObject);
break;
case 'Lock':
this.onLockItem(currentObject);
break;
case 'Comment':
this.onComnentItem();
break;
case 'History':
this.onHistory(currentObject);
break;
case 'Details':
this.onDetails(currentObject);
break;
case 'Access Log':
this.onAccessLog();
break;
case 'Open via Client':
this.onOpenViaClient(currentObject);
break;
default:
break;
}
}
getDirentPath = (dirent) => {
let path = this.props.path;
return path === '/' ? path + dirent.name : path + '/' + dirent.name;
}
closeZipDialog = () => {
this.setState({
isZipDialogOpen: false
});
}
onDetails = (dirent) => {
this.props.onGridItemClick(dirent)
this.props.showDirentDetail();
}
onItemDownload = (currentObject, e) => {
e.nativeEvent.stopImmediatePropagation();
let dirent = currentObject;
let repoID = this.props.repoID;
let direntPath = this.getDirentPath(dirent);
if (dirent.type === 'dir') {
this.setState({
isZipDialogOpen: true
});
} else {
let url = URLDecorator.getUrl({type: 'download_file_url', repoID: repoID, filePath: direntPath});
location.href = url;
}
}
onItemRenameToggle = () => {
this.setState({
isRenameDialogShow: !this.state.isRenameDialogShow,
});
}
onItemMoveToggle = () => {
this.setState({isMoveDialogShow: !this.state.isMoveDialogShow});
}
onItemCopyToggle = () => {
this.setState({isCopyDialogShow: !this.state.isCopyDialogShow});
}
onPermissionItem = () => {
}
onLockItem = (currentObject) => {
let repoID = this.props.repoID;
let filePath = this.getDirentPath(currentObject);
seafileAPI.lockfile(repoID, filePath).then(() => {
this.props.updateDirent(currentObject, 'is_locked', true);
this.props.updateDirent(currentObject, 'locked_by_me', true);
});
}
onUnlockItem = (currentObject) => {
let repoID = this.props.repoID;
let filePath = this.getDirentPath(currentObject);
seafileAPI.unlockfile(repoID, filePath).then(() => {
this.props.updateDirent(currentObject, 'is_locked', false);
this.props.updateDirent(currentObject, 'locked_by_me', false);
});
}
onComnentItem = () => {
}
onHistory = (currentObject) => {
let repoID = this.props.repoID;
let filePath = this.getDirentPath(currentObject);
let url = URLDecorator.getUrl({type: 'file_revisions', repoID: repoID, filePath: filePath});
location.href = url;
}
onAccessLog = () => {
}
onOpenViaClient = (currentObject) => {
let repoID = this.props.repoID;
let filePath = this.getDirentPath(currentObject);
let url = URLDecorator.getUrl({type: 'open_via_client', repoID: repoID, filePath: filePath});
location.href = url;
}
onItemRename = (newName) => {
this.setState({
isRenameDialogShow: !this.state.isRenameDialogShow,
});
this.props.onItemRename(this.state.dirent, newName)
}
prepareImageItem = (item) => {
const useThumbnail = !this.repoEncrypted;
const name = item.name;
const fileExt = name.substr(name.lastIndexOf('.') + 1).toLowerCase();
const isGIF = fileExt == 'gif';
const path = Utils.encodePath(Utils.joinPath(this.props.path, name));
const repoID = this.props.repoID;
let src;
if (useThumbnail && !isGIF) {
src = `${siteRoot}thumbnail/${repoID}/${thumbnailSizeForOriginal}${path}`;
} else {
src = `${siteRoot}repo/${repoID}/raw${path}`;
}
return {
'name': name,
'url': `${siteRoot}lib/${repoID}/file${path}`,
'src': src
};
}
showImagePopup = (curItem) => {
let items = this.props.direntList.filter((item) => {
return Utils.imageCheck(item.name);
});
const imageItems = items.map((item) => {
return this.prepareImageItem(item);
});
this.setState({
isImagePopupOpen: true,
imageItems: imageItems,
imageIndex: items.indexOf(curItem)
});
}
closeImagePopup = () => {
this.setState({isImagePopupOpen: false})
}
moveToPrevImage = () => {
const imageItemsLength = this.state.imageItems.length;
this.setState((prevState) => ({
imageIndex: (prevState.imageIndex + imageItemsLength - 1) % imageItemsLength
}));
}
moveToNextImage = () => {
const imageItemsLength = this.state.imageItems.length;
this.setState((prevState) => ({
imageIndex: (prevState.imageIndex + 1) % imageItemsLength
}));
}
checkDuplicatedName = (newName) => {
let direntList = this.props.direntList;
let isDuplicated = direntList.some(object => {
return object.name === newName;
});
return isDuplicated;
}
gridContainerClick = () => {
if (!this.props.isDirentDetailShow) {
this.props.onGridItemClick(null);
}
}
handleContextClick = (event, currentObject) => {
event.preventDefault();
event.stopPropagation();
let x = event.clientX || (event.touches && event.touches[0].pageX);
let y = event.clientY || (event.touches && event.touches[0].pageY);
if (this.props.posX) {
x -= this.props.posX;
}
if (this.props.posY) {
y -= this.props.posY;
}
hideMenu();
let menuList = this.getDirentItemMenuList(currentObject, true);
this.setState({dirent: currentObject});
let showMenuConfig = {
id: 'grid-item-contextmenu',
position: { x, y },
target: event.target,
currentObject: currentObject,
menuList: menuList,
};
showMenu(showMenuConfig);
}
getDirentItemMenuList = (dirent, isContextmenu) => {
let isRepoOwner = this.isRepoOwner;
let currentRepoInfo = this.props.currentRepoInfo;
let can_set_folder_perm = folderPermEnabled && ((isRepoOwner && currentRepoInfo.has_been_shared_out) || currentRepoInfo.is_admin);
let type = dirent.type;
let permission = dirent.permission;
let menuList = [];
let contextmenuList = [];
if (isContextmenu) {
let { SHARE, DOWNLOAD, DELETE } = TextTranslation;
contextmenuList = this.props.showShareBtn ? [SHARE, DOWNLOAD, DELETE, 'Divider'] : [DOWNLOAD, DELETE, 'Divider'];
}
let { RENAME, MOVE, COPY, PERMISSION, DETAILS, OPEN_VIA_CLIENT, LOCK, UNLOCK, COMMENT, HISTORY, ACCESS_LOG } = TextTranslation;
if (type === 'dir' && permission === 'rw') {
if (can_set_folder_perm) {
menuList = [...contextmenuList, RENAME, MOVE, COPY, 'Divider', PERMISSION, DETAILS, 'Divider', OPEN_VIA_CLIENT];
} else {
menuList = [...contextmenuList, RENAME, MOVE, COPY, 'Divider', DETAILS, 'Divider', OPEN_VIA_CLIENT];
}
return menuList;
}
if (type === 'dir' && permission === 'r') {
menuList = currentRepoInfo.encrypted ? [...contextmenuList, COPY, DETAILS] : [DETAILS];
return menuList;
}
if (type === 'file' && permission === 'rw') {
menuList = [...contextmenuList];
if (!dirent.is_locked || (dirent.is_locked && dirent.locked_by_me)) {
menuList.push(RENAME);
menuList.push(MOVE);
}
menuList.push(COPY);
if (isPro) {
if (dirent.is_locked) {
if (dirent.locked_by_me || (dirent.lock_owner === 'OnlineOffice' && permission === 'rw')) {
menuList.push(UNLOCK);
}
} else {
menuList.push(LOCK);
}
}
menuList.push('Divider');
if (enableFileComment) {
menuList.push(COMMENT);
}
menuList.push(HISTORY);
if (fileAuditEnabled) {
menuList.push(ACCESS_LOG);
}
menuList.push(DETAILS);
menuList.push('Divider');
menuList.push(OPEN_VIA_CLIENT);
return menuList;
}
if (type === 'file' && permission === 'r') {
menuList = [...contextmenuList];
if (!currentRepoInfo.encrypted) {
menuList.push(COPY);
}
if (enableFileComment) {
menuList.push(COMMENT);
}
menuList.push(HISTORY);
menuList.push(DETAILS);
return menuList;
}
}
render() {
let {direntList, path} = this.props;
let dirent = this.state.dirent;
let direntPath = Utils.joinPath(path, dirent.name);
if (this.props.isDirentListLoading) {
return (<Loading />);
}
return (
<Fragment>
<ul className="grid-view" onClick={this.gridContainerClick}>
{
direntList.length !== 0 && direntList.map((dirent, index) => {
return (
<DirentGridItem
key={index}
dirent={dirent}
repoID={this.props.repoID}
path={this.props.path}
onItemClick={this.props.onItemClick}
currentRepoInfo={this.props.currentRepoInfo}
showImagePopup={this.showImagePopup}
handleContextClick={this.handleContextClick}
onItemMove={this.props.onItemMove}
/>
)
})
}
</ul>
<ContextMenu
id={'grid-item-contextmenu'}
onMenuItemClick={this.onMenuItemClick}
/>
{this.state.isMoveDialogShow &&
<MoveDirentDialog
path={this.props.path}
repoID={this.props.repoID}
repoEncrypted={this.props.currentRepoInfo.encrypted}
isMutipleOperation={this.state.isMutipleOperation}
onItemMove={this.props.onItemMove}
onCancelMove={this.onMoveToggle}
dirent={this.state.dirent}
/>
}
{this.state.isZipDialogOpen &&
<ModalPortal>
<ZipDownloadDialog
repoID={this.props.repoID}
path={this.props.path}
target={dirent.name}
toggleDialog={this.closeZipDialog}
/>
</ModalPortal>
}
{this.state.isCopyDialogShow &&
<CopyDirentDialog
path={this.props.path}
repoID={this.props.repoID}
repoEncrypted={this.props.currentRepoInfo.encrypted}
isMutipleOperation={this.state.isMutipleOperation}
onItemCopy={this.props.onItemCopy}
onCancelCopy={this.onCopyToggle}
dirent={this.state.dirent}
/>
}
{this.state.isShareDialogShow &&
<ModalPortal>
<ShareDialog
itemType={dirent.type}
itemName={dirent.name}
itemPath={direntPath}
userPerm={dirent.permission}
repoID={this.props.repoID}
repoEncrypted={false}
enableDirPrivateShare={this.props.enableDirPrivateShare}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
toggleDialog={this.closeSharedDialog}
/>
</ModalPortal>
}
{this.state.isRenameDialogShow && (
<ModalPortal>
<Rename
dirent={this.state.dirent}
onRename={this.onItemRename}
checkDuplicatedName={this.checkDuplicatedName}
toggleCancel={this.onItemRenameToggle}
/>
</ModalPortal>
)}
{this.state.isImagePopupOpen && (
<ModalPortal>
<ImageDialog
imageItems={this.state.imageItems}
imageIndex={this.state.imageIndex}
closeImagePopup={this.closeImagePopup}
moveToPrevImage={this.moveToPrevImage}
moveToNextImage={this.moveToNextImage}
/>
</ModalPortal>
)}
</Fragment>
)
}
}
DirentGridView.propTypes = propTypes;
export default DirentGridView;

View File

@@ -24,7 +24,7 @@ class ViewModeToolbar extends React.Component {
<React.Fragment>
<div className="view-mode btn-group">
<button className={`${baseClass} sf2-icon-list-view ${this.props.currentMode === 'list' ? 'current-mode' : ''}`} id='list' title={gettext('List')} onClick={this.switchViewMode}></button>
{/* <button className={`${baseClass} sf2-icon-grid-view ${this.props.currentMode === 'grid' ? 'current-mode' : ''}`} id='grid' title={gettext('Grid')} onClick={this.switchViewMode}></button> */}
<button className={`${baseClass} sf2-icon-grid-view ${this.props.currentMode === 'grid' ? 'current-mode' : ''}`} id='grid' title={gettext('Grid')} onClick={this.switchViewMode}></button>
<button className={`${baseClass} sf2-icon-two-columns ${this.props.currentMode === 'column' ? 'current-mode' : ''}`} id='column' title={gettext('Column')} onClick={this.switchViewMode}></button>
</div>
<div className="detail-btn btn-group">

View File

@@ -0,0 +1,74 @@
.grid-view {
padding-top: 10px;
list-style: none;
display: flex;
flex-wrap: wrap;
flex: 1;
align-content: flex-start;
}
.grid-item {
width: 134px;
padding: 10px 4px;
text-align: center;
position: relative;
line-height: 0;
}
.grid-item:hover .grid-file-img-link {
background: #f8f8f8;
}
.grid-item:hover a {
color: #eb8205;
}
.grid-file-img-link {
width: 100px;
height: 100px;
margin: 0 auto 6px;
position: relative;
border-radius: 3px;
font-size: 0;
text-align: center;
line-height: 0;
padding: 2px;
}
.grid-file-img-link .thumbnail {
max-width: 96px;
max-height: 96px;
padding: 1px;
border: 1px solid #ddd;
border-radius: 3px;
}
.grid-file-img-link::before {
content: '';
display: inline-block;
vertical-align: middle;
height: 100%;
}
.grid-file-name {
display: inline-block;
max-width: 100%;
overflow: hidden;
word-break: keep-all;
white-space: nowrap;
text-overflow: ellipsis;
line-height: 17px;
font-size: 14px;
}
.grid-file-name-link {
color: #333;
font-size: 0.875rem;
}
.grid-file-locked-icon {
position: absolute;
bottom: 5px;
right: 10px;
width: 16px;
}

View File

@@ -114,6 +114,10 @@ class LibContentContainer extends React.Component {
this.props.closeDirentDetail();
}
onGridItemClick = (dirent) => {
this.setState({currentDirent: dirent});
}
// on '<tr>'
onDirentClick = (dirent) => {
this.setState({currentDirent: dirent});
@@ -203,6 +207,32 @@ class LibContentContainer extends React.Component {
)}
{this.props.currentMode === 'grid' && (
<DirGridView
path={this.props.path}
repoID={repoID}
currentRepoInfo={this.props.currentRepoInfo}
repoPermission={this.props.repoPermission}
isGroupOwnedRepo={this.props.isGroupOwnedRepo}
enableDirPrivateShare={this.props.enableDirPrivateShare}
onRenameNode={this.props.onRenameNode}
isRepoInfoBarShow={isRepoInfoBarShow}
usedRepoTags={this.props.usedRepoTags}
readmeMarkdown={this.props.readmeMarkdown}
draftCounts={this.props.draftCounts}
updateUsedRepoTags={this.props.updateUsedRepoTags}
isDirentListLoading={this.props.isDirentListLoading}
direntList={this.props.direntList}
onAddFile={this.props.onAddFile}
onItemClick={this.onItemClick}
onItemDelete={this.props.onItemDelete}
onItemMove={this.props.onItemMove}
onItemCopy={this.props.onItemCopy}
updateDirent={this.props.updateDirent}
onAddFolder={this.props.onAddFolder}
showShareBtn={this.props.showShareBtn}
showDirentDetail={this.props.showDirentDetail}
onGridItemClick={this.onGridItemClick}
isDirentDetailShow={this.props.isDirentDetailShow}
onItemRename={this.props.onItemRename}
/>
)}
{this.props.currentMode === 'column' && (