1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-13 13:50:07 +00:00

feat: detail resize width (#6507)

Co-authored-by: 杨国璇 <ygx@Hello-word.local>
This commit is contained in:
杨国璇
2024-08-07 14:01:01 +08:00
committed by GitHub
parent c305f93a3f
commit 4dc0df7093
21 changed files with 199 additions and 103 deletions

View File

@@ -6,7 +6,7 @@
font-size: 14px; font-size: 14px;
} }
.dirent-detail-item .dirent-detail-item-name-container { .dirent-detail-item .dirent-detail-item-name {
width: 160px; width: 160px;
padding: 7px 6px; padding: 7px 6px;
min-height: 34px; min-height: 34px;
@@ -16,7 +16,7 @@
line-height: 1.4; line-height: 1.4;
} }
.dirent-detail-item .dirent-detail-item-name-container .sf-metadata-icon { .dirent-detail-item .dirent-detail-item-name .sf-metadata-icon {
margin-right: 6px; margin-right: 6px;
font-size: 14px; font-size: 14px;
fill: #999; fill: #999;
@@ -34,7 +34,7 @@
cursor: pointer; cursor: pointer;
} }
.dirent-detail-item .dirent-detail-item-name-container:hover, .dirent-detail-item .dirent-detail-item-name:hover,
.dirent-detail-item .dirent-detail-item-value:hover { .dirent-detail-item .dirent-detail-item-value:hover {
background-color: #F5F5F5; background-color: #F5F5F5;
border-radius: 3px; border-radius: 3px;
@@ -55,3 +55,20 @@
color: #666; color: #666;
font-size: 14px; font-size: 14px;
} }
/* */
.cur-view-detail-small .dirent-detail-item .dirent-detail-item-name {
width: 44%;
}
.cur-view-detail-small .dirent-detail-item .dirent-detail-item-value {
width: 56%;
}
.cur-view-detail-large .dirent-detail-item .dirent-detail-item-name {
width: 160px;
}
.cur-view-detail-large .dirent-detail-item .dirent-detail-item-value {
flex: 1;
}

View File

@@ -15,9 +15,9 @@ const DetailItem = ({ field, value, valueId, valueClick, children, ...params })
return ( return (
<div className="dirent-detail-item"> <div className="dirent-detail-item">
<div className="dirent-detail-item-name-container"> <div className="dirent-detail-item-name">
<Icon iconName={icon} /> <Icon iconName={icon} />
<span className="dirent-detail-item-name">{field.name}</span> <span className="dirent-detail-item-name-value">{field.name}</span>
</div> </div>
<div className={classnames('dirent-detail-item-value', { 'editable': valueClick })} id={valueId} onClick={valueClick}> <div className={classnames('dirent-detail-item-value', { 'editable': valueClick })} id={valueId} onClick={valueClick}>
{children ? children : (<Formatter { ...params } field={field} value={value} />)} {children ? children : (<Formatter { ...params } field={field} value={value} />)}

View File

@@ -0,0 +1,8 @@
.detail-body {
flex: 1;
display: flex;
flex-direction: column;
overflow-y: auto;
overflow-x: hidden;
padding: 16px;
}

View File

@@ -0,0 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import './index.css';
const Body = ({ className, children }) => {
return (
<div className={classnames('detail-body dirent-info', className)}>
{children}
</div>
);
};
Body.propTypes = {
className: PropTypes.string,
children: PropTypes.any,
};
export default Body;

View File

@@ -0,0 +1,21 @@
@keyframes move {
from {
right: -500px;
opacity: 0.5;
}
to {
right: 0px;
opacity: 1;
}
}
.cur-view-detail {
display: flex;
flex-direction: column;
background-color: #fff;
width: 400px;
height: 100%;
border-left: 1px solid #eee;
animation: move .5s ease-in-out 1;
position: relative;
}

View File

@@ -0,0 +1,79 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import ResizeBar from '../../../resize-bar';
import { DRAG_HANDLER_HEIGHT } from '../../../resize-bar/constants';
import './index.css';
const Detail = ({ children, className }) => {
const [width, setWidth] = useState(300);
const [inResizing, setResizing] = useState(false);
const resizeBarRef = useRef(null);
const dragHandlerRef = useRef(null);
const onResizeMouseMove = useCallback((e) => {
const newWidth = Math.max(Math.min(window.innerWidth - e.clientX, 600), 300);
if (width === newWidth) return;
setWidth(newWidth);
}, [width]);
const onResizeMouseUp = useCallback(() => {
window.removeEventListener('mousemove', onResizeMouseMove);
window.removeEventListener('mouseup', onResizeMouseUp);
inResizing && setResizing(false);
localStorage.setItem('sf_cur_view_detail_width', width);
}, [width, inResizing, onResizeMouseMove]);
const onResizeMouseDown = useCallback(() => {
window.addEventListener('mouseup', onResizeMouseUp);
window.addEventListener('mousemove', onResizeMouseMove);
setResizing(true);
}, [onResizeMouseUp, onResizeMouseMove]);
const setDragHandlerTop = useCallback((top) => {
dragHandlerRef.current.style.top = top + 'px';
}, []);
const onResizeMouseOver = useCallback((event) => {
if (!dragHandlerRef.current) return;
const { top } = resizeBarRef.current.getBoundingClientRect();
const dragHandlerRefTop = event.pageY - top - DRAG_HANDLER_HEIGHT / 2;
setDragHandlerTop(dragHandlerRefTop);
}, [setDragHandlerTop]);
useEffect(() => {
const width = localStorage.getItem('sf_cur_view_detail_width', 300);
setWidth(width);
}, []);
return (
<div
className={classnames('cur-view-detail', className, {
'cur-view-detail-small': width < 400,
'cur-view-detail-large': width > 400
})}
style={{ width }}
// onMouseMove={inResizing ? onResizeMouseMove : null}
// onMouseUp={onResizeMouseUp}
>
{children}
<ResizeBar
resizeBarRef={resizeBarRef}
dragHandlerRef={dragHandlerRef}
resizeBarStyle={{ left: -1 }}
dragHandlerStyle={{ height: DRAG_HANDLER_HEIGHT }}
onResizeMouseDown={onResizeMouseDown}
onResizeMouseOver={onResizeMouseOver}
/>
</div>
);
};
Detail.propTypes = {
className: PropTypes.string,
children: PropTypes.any,
};
export default Detail;

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Icon from '../../icon'; import Icon from '../../../icon';
import './index.css'; import './index.css';

View File

@@ -0,0 +1,9 @@
import Detail from './detail';
import Header from './header';
import Body from './body';
export {
Detail,
Header,
Body,
};

View File

@@ -7,12 +7,12 @@ import { gettext } from '../../../utils/constants';
import { MetadataDetails } from '../../../metadata'; import { MetadataDetails } from '../../../metadata';
const DirDetails = ({ repoID, repoInfo, dirent, path, direntDetail, ...params }) => { const DirDetails = ({ repoID, repoInfo, dirent, path, direntDetail, ...params }) => {
const parent = useMemo(() => getFileParent(repoInfo, dirent, path), [repoInfo, dirent, path]); const parent = useMemo(() => getFileParent(dirent, path), [dirent, path]);
const direntPath = useMemo(() => getDirentPath(dirent, path), [dirent, path]); const direntPath = useMemo(() => getDirentPath(dirent, path), [dirent, path]);
return ( return (
<> <>
<DetailItem field={{ type: CellType.TEXT, name: gettext('Parent') }} value={parent} /> <DetailItem field={{ type: CellType.TEXT, name: gettext('Parent folder') }} value={parent} />
<DetailItem field={{ type: 'size', name: gettext('Size') }} value={repoInfo.size} /> <DetailItem field={{ type: 'size', name: gettext('Size') }} value={repoInfo.size} />
<DetailItem field={{ type: CellType.CREATOR, name: gettext('Creator') }} value={repoInfo.owner_email} collaborators={[{ <DetailItem field={{ type: CellType.CREATOR, name: gettext('Creator') }} value={repoInfo.owner_email} collaborators={[{
name: repoInfo.owner_name, name: repoInfo.owner_name,

View File

@@ -14,7 +14,7 @@ import ObjectUtils from '../../../metadata/metadata-view/utils/object-utils';
const FileDetails = React.memo(({ repoID, repoInfo, dirent, path, direntDetail, onFileTagChanged, repoTags, fileTagList, ...params }) => { const FileDetails = React.memo(({ repoID, repoInfo, dirent, path, direntDetail, onFileTagChanged, repoTags, fileTagList, ...params }) => {
const [isEditFileTagShow, setEditFileTagShow] = useState(false); const [isEditFileTagShow, setEditFileTagShow] = useState(false);
const parent = useMemo(() => getFileParent(repoInfo, dirent, path), [repoInfo, dirent, path]); const parent = useMemo(() => getFileParent(dirent, path), [dirent, path]);
const direntPath = useMemo(() => getDirentPath(dirent, path), [dirent, path]); const direntPath = useMemo(() => getDirentPath(dirent, path), [dirent, path]);
const tagListTitleID = useMemo(() => `detail-list-view-tags-${uuidV4()}`, []); const tagListTitleID = useMemo(() => `detail-list-view-tags-${uuidV4()}`, []);
@@ -28,7 +28,7 @@ const FileDetails = React.memo(({ repoID, repoInfo, dirent, path, direntDetail,
return ( return (
<> <>
<DetailItem field={{ type: CellType.TEXT, name: gettext('Parent') }} value={parent} /> <DetailItem field={{ type: CellType.TEXT, name: gettext('Parent folder') }} value={parent} />
<DetailItem field={{ type: 'size', name: gettext('Size') }} value={Utils.bytesToSize(direntDetail.size)} /> <DetailItem field={{ type: 'size', name: gettext('Size') }} value={Utils.bytesToSize(direntDetail.size)} />
<DetailItem field={{ type: CellType.LAST_MODIFIER, name: gettext('Last modifier') }} value={direntDetail.last_modifier_email} collaborators={[{ <DetailItem field={{ type: CellType.LAST_MODIFIER, name: gettext('Last modifier') }} value={direntDetail.last_modifier_email} collaborators={[{
name: direntDetail.last_modifier_name, name: direntDetail.last_modifier_name,

View File

@@ -1,4 +1,4 @@
.detail-container .detail-image-thumbnail { .detail-body .detail-image-thumbnail {
height: 144px; height: 144px;
width: 100%; width: 100%;
flex-shrink: 0; flex-shrink: 0;
@@ -9,7 +9,7 @@
overflow: hidden; overflow: hidden;
} }
.detail-container .detail-image-thumbnail .thumbnail { .detail-body .detail-image-thumbnail .thumbnail {
height: 100%; height: 100%;
width: 100%; width: 100%;
border: 0; border: 0;
@@ -24,6 +24,6 @@
max-height: 100%; max-height: 100%;
} }
.detail-container .empty-tip-text { .detail-body .empty-tip-text {
color: #666; color: #666;
} }

View File

@@ -5,7 +5,7 @@ import { seafileAPI } from '../../../utils/seafile-api';
import { Utils } from '../../../utils/utils'; import { Utils } from '../../../utils/utils';
import toaster from '../../toast'; import toaster from '../../toast';
import Dirent from '../../../models/dirent'; import Dirent from '../../../models/dirent';
import Header from '../header'; import { Detail, Header, Body } from '../detail';
import DirDetails from './dir-details'; import DirDetails from './dir-details';
import FileDetails from './file-details'; import FileDetails from './file-details';
import ObjectUtils from '../../../metadata/metadata-view/utils/object-utils'; import ObjectUtils from '../../../metadata/metadata-view/utils/object-utils';
@@ -106,9 +106,9 @@ class DirentDetails extends React.Component {
} }
return ( return (
<div className="detail-container"> <Detail>
<Header title={direntName} icon={smallIconUrl} onClose={this.props.onClose} /> <Header title={direntName} icon={smallIconUrl} onClose={this.props.onClose} />
<div className="detail-body dirent-info"> <Body>
{isImg && ( {isImg && (
<div className="detail-image-thumbnail"> <div className="detail-image-thumbnail">
<img src={bigIconUrl} alt="" className="thumbnail" /> <img src={bigIconUrl} alt="" className="thumbnail" />
@@ -146,8 +146,8 @@ class DirentDetails extends React.Component {
)} )}
</div> </div>
)} )}
</div> </Body>
</div> </Detail>
); );
} }
} }

View File

@@ -6,10 +6,10 @@ export const getDirentPath = (dirent, path) => {
return Utils.joinPath(path, dirent.name); return Utils.joinPath(path, dirent.name);
}; };
export const getFileParent = (repoInfo, dirent, path) => { export const getFileParent = (dirent, path) => {
const direntPath = getDirentPath(dirent, path); const direntPath = getDirentPath(dirent, path);
const position = repoInfo.repo_name; if (direntPath === '/') return '/';
if (direntPath === '/') return position;
const index = direntPath.lastIndexOf('/'); const index = direntPath.lastIndexOf('/');
return position + direntPath.slice(0, index); const positionPath = direntPath.slice(0, index);
return positionPath || '/';
}; };

View File

@@ -1,33 +0,0 @@
.detail-container {
flex: 1;
display: flex;
flex-direction: column;
}
.detail-container .detail-content {
display: flex;
flex-direction: column;
}
.detail-body {
flex: 1;
display: flex;
flex-direction: column;
overflow-y: auto;
overflow-x: hidden;
padding: 16px;
}
.dirent-info .img {
height: 10rem;
padding: 0.5rem 0;
display: flex;
justify-content: center;
align-items: center;
}
.dirent-info .img .thumbnail {
max-width: calc(100% - 4px);
max-height: 100%;
display: inline-block;
}

View File

@@ -2,8 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import LibDetail from './lib-details'; import LibDetail from './lib-details';
import DirentDetail from './dirent-details'; import DirentDetail from './dirent-details';
import './index.css';
import ObjectUtils from '../../metadata/metadata-view/utils/object-utils'; import ObjectUtils from '../../metadata/metadata-view/utils/object-utils';
const Index = React.memo(({ repoID, path, dirent, currentRepoInfo, repoTags, fileTags, onClose, onFileTagChanged }) => { const Index = React.memo(({ repoID, path, dirent, currentRepoInfo, repoTags, fileTags, onClose, onFileTagChanged }) => {

View File

@@ -4,7 +4,7 @@ import { Utils } from '../../../utils/utils';
import { gettext } from '../../../utils/constants'; import { gettext } from '../../../utils/constants';
import { seafileAPI } from '../../../utils/seafile-api'; import { seafileAPI } from '../../../utils/seafile-api';
import toaster from '../../toast'; import toaster from '../../toast';
import Header from '../header'; import { Detail, Header, Body } from '../detail';
import Repo from '../../../models/repo'; import Repo from '../../../models/repo';
import Loading from '../../loading'; import Loading from '../../loading';
import DetailItem from '../detail-item'; import DetailItem from '../detail-item';
@@ -28,9 +28,9 @@ const LibDetail = React.memo(({ currentRepoInfo, onClose }) => {
}, [currentRepoInfo.repo_id]); }, [currentRepoInfo.repo_id]);
return ( return (
<div className="detail-container"> <Detail>
<Header title={currentRepoInfo.repo_name} icon={smallIconUrl} onClose={onClose} /> <Header title={currentRepoInfo.repo_name} icon={smallIconUrl} onClose={onClose} />
<div className="detail-body dirent-info"> <Body>
{isLoading ? ( {isLoading ? (
<div className="w-100 h-100 d-flex algin-items-center justify-content-center"><Loading /></div> <div className="w-100 h-100 d-flex algin-items-center justify-content-center"><Loading /></div>
) : ( ) : (
@@ -46,8 +46,8 @@ const LibDetail = React.memo(({ currentRepoInfo, onClose }) => {
<DetailItem field={{ type: CellType.MTIME, name: gettext('Last modified time') }} value={repo.last_modified} /> <DetailItem field={{ type: CellType.MTIME, name: gettext('Last modified time') }} value={repo.last_modified} />
</div> </div>
)} )}
</div> </Body>
</div> </Detail>
); );
}, (props, nextProps) => { }, (props, nextProps) => {

View File

@@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { RESIZE_BAR } from '../../constants/zIndexes'; import { RESIZE_BAR } from '../../constants/zIndexes';
import './index.css'; import './index.css';
function ResizeBar(props) { function ResizeBar(props) {

View File

@@ -178,33 +178,6 @@
font-size: 14px; font-size: 14px;
} }
.cur-view-detail {
display: block;
position: absolute;
right: 0;
background-color: #fff;
width: 400px;
height: 100%;
box-shadow: -1px 0 3px 0 #ccc;
animation: move .5s ease-in-out 1;
z-index: 50;
}
@keyframes move {
from {
right: -500px;
opacity: 0.5;
}
to {
right: 0px;
opacity: 1;
}
}
.cur-view-detail .detail-container {
height: 100%;
}
/* for reach/router */ /* for reach/router */
.reach-router, .reach-router,
div[tabindex="-1"][role="group"] { div[tabindex="-1"][role="group"] {

View File

@@ -22,9 +22,10 @@
.dir-column-view { .dir-column-view {
display: flex; display: flex;
width: 100%;
height: 100%; height: 100%;
position: relative; position: relative;
flex: 1;
overflow-x: hidden;
} }
@media (max-width: 767px) { @media (max-width: 767px) {

View File

@@ -250,7 +250,11 @@ class LibContentContainer extends React.Component {
switchViewMode={this.props.switchViewMode} switchViewMode={this.props.switchViewMode}
/> />
</div> </div>
<div className={`cur-view-content lib-content-container ${this.props.isTreePanelShown ? 'view-mode-container' : ''}`} onScroll={this.onItemsScroll}> <div
className={`cur-view-content lib-content-container ${this.props.isTreePanelShown ? 'view-mode-container' : ''}`}
onScroll={this.onItemsScroll}
>
{!this.props.pathExist && this.errMessage} {!this.props.pathExist && this.errMessage}
{this.props.pathExist && ( {this.props.pathExist && (
<DirColumnView <DirColumnView
@@ -318,7 +322,6 @@ class LibContentContainer extends React.Component {
/> />
)} )}
{this.props.isDirentDetailShow && ( {this.props.isDirentDetailShow && (
<div className="cur-view-detail">
<Detail <Detail
path={path} path={path}
repoID={repoID} repoID={repoID}
@@ -329,7 +332,6 @@ class LibContentContainer extends React.Component {
onFileTagChanged={this.props.onFileTagChanged} onFileTagChanged={this.props.onFileTagChanged}
onClose={this.props.closeDirentDetail} onClose={this.props.closeDirentDetail}
/> />
</div>
)} )}
</div> </div>
</div> </div>