mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-08 10:22:46 +00:00
feat: metadata details settings (#7145)
* feat: metadata details settings * feat: optimize code * feat: optimize code * feat: optimize code * feat: optimize code * feat: optimize code * feat: optimize code * feat: optimize code --------- Co-authored-by: 杨国璇 <ygx@Hello-word.local> Co-authored-by: 杨国璇 <ygx@192.168.1.6>
This commit is contained in:
@@ -9,28 +9,11 @@
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.detail-header .detail-title {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
width: 0; /* prevent strut flex layout */
|
||||
}
|
||||
|
||||
.detail-header .detail-title .detail-header-icon-container {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
.detail-header .detail-control-container {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-header .detail-title .name {
|
||||
margin: 0 0.5rem 0 6px;
|
||||
line-height: 1.5rem;
|
||||
vertical-align: middle;
|
||||
font-size: 1rem;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.detail-header .detail-control {
|
||||
|
@@ -1,21 +1,20 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Icon from '../../../icon';
|
||||
import Title from './title';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const Header = ({ title, icon, iconSize = 32, onClose, component = {} }) => {
|
||||
const Header = ({ title, icon, iconSize = 32, onClose, children, component = {} }) => {
|
||||
const { closeIcon } = component;
|
||||
return (
|
||||
<div className="detail-header">
|
||||
<div className="detail-title dirent-title">
|
||||
<div className="detail-header-icon-container">
|
||||
<img src={icon} width={iconSize} height={iconSize} alt="" />
|
||||
<Title title={title} icon={icon} iconSize={iconSize} />
|
||||
<div className="detail-control-container">
|
||||
{children}
|
||||
<div className="detail-control" onClick={onClose}>
|
||||
{closeIcon ? closeIcon : <Icon symbol="close" className="detail-control-close" />}
|
||||
</div>
|
||||
<span className="name ellipsis" title={title}>{title}</span>
|
||||
</div>
|
||||
<div className="detail-control" onClick={onClose}>
|
||||
{closeIcon ? closeIcon : <Icon symbol="close" className="detail-control-close" />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -26,6 +25,7 @@ Header.propTypes = {
|
||||
icon: PropTypes.string.isRequired,
|
||||
iconSize: PropTypes.number,
|
||||
component: PropTypes.object,
|
||||
children: PropTypes.any,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
@@ -0,0 +1,23 @@
|
||||
.detail-header .detail-title {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
width: 0; /* prevent strut flex layout */
|
||||
}
|
||||
|
||||
.detail-header .detail-title .detail-header-icon-container {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-header .detail-title .name {
|
||||
margin: 0 0.5rem 0 6px;
|
||||
line-height: 1.5rem;
|
||||
vertical-align: middle;
|
||||
font-size: 1rem;
|
||||
color: #212529;
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const Title = ({ icon, iconSize, title }) => {
|
||||
|
||||
return (
|
||||
<div className="detail-title dirent-title">
|
||||
<div className="detail-header-icon-container">
|
||||
<img src={icon} width={iconSize} height={iconSize} alt="" />
|
||||
</div>
|
||||
<span className="name ellipsis" title={title}>{title}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Title.propTypes = {
|
||||
icon: PropTypes.string,
|
||||
iconSize: PropTypes.number,
|
||||
title: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Title;
|
@@ -1,16 +1,14 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Formatter } from '@seafile/sf-metadata-ui-component';
|
||||
import { getDirentPath } from './utils';
|
||||
import DetailItem from '../detail-item';
|
||||
import { CellType } from '../../../metadata/constants';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import { MetadataDetails } from '../../../metadata';
|
||||
import { useMetadataStatus } from '../../../hooks';
|
||||
|
||||
const DirDetails = ({ repoID, repoInfo, dirent, path, direntDetail }) => {
|
||||
const direntPath = useMemo(() => getDirentPath(dirent, path), [dirent, path]);
|
||||
const { enableMetadata } = useMetadataStatus();
|
||||
const DirDetails = ({ direntDetail }) => {
|
||||
const { enableMetadata, enableMetadataManagement } = useMetadataStatus();
|
||||
const lastModifiedTimeField = useMemo(() => {
|
||||
return { type: CellType.MTIME, name: gettext('Last modified time') };
|
||||
}, []);
|
||||
@@ -20,8 +18,8 @@ const DirDetails = ({ repoID, repoInfo, dirent, path, direntDetail }) => {
|
||||
<DetailItem field={lastModifiedTimeField} className="sf-metadata-property-detail-formatter">
|
||||
<Formatter field={lastModifiedTimeField} value={direntDetail.mtime} />
|
||||
</DetailItem>
|
||||
{window.app.pageOptions.enableMetadataManagement && enableMetadata && (
|
||||
<MetadataDetails repoID={repoID} repoInfo={repoInfo} filePath={direntPath} direntType="dir" />
|
||||
{enableMetadataManagement && enableMetadata && (
|
||||
<MetadataDetails />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
@@ -10,7 +10,7 @@ import { gettext } from '../../../../utils/constants';
|
||||
import EditFileTagPopover from '../../../popover/edit-filetag-popover';
|
||||
import FileTagList from '../../../file-tag-list';
|
||||
import { Utils } from '../../../../utils/utils';
|
||||
import { MetadataDetails } from '../../../../metadata';
|
||||
import { MetadataDetails, useMetadataDetails } from '../../../../metadata';
|
||||
import ObjectUtils from '../../../../metadata/utils/object-utils';
|
||||
import { getCellValueByColumn, getDateDisplayString, decimalToExposureTime } from '../../../../metadata/utils/cell';
|
||||
import Collapse from './collapse';
|
||||
@@ -57,10 +57,10 @@ const getImageInfoValue = (key, value) => {
|
||||
}
|
||||
};
|
||||
|
||||
const FileDetails = React.memo(({ repoID, repoInfo, dirent, path, direntDetail, onFileTagChanged, repoTags, fileTagList }) => {
|
||||
const FileDetails = React.memo(({ repoID, dirent, path, direntDetail, onFileTagChanged, repoTags, fileTagList }) => {
|
||||
const [isEditFileTagShow, setEditFileTagShow] = useState(false);
|
||||
const { enableMetadata } = useMetadataStatus();
|
||||
const [record, setRecord] = useState(null);
|
||||
const { enableMetadataManagement, enableMetadata } = useMetadataStatus();
|
||||
const { record } = useMetadataDetails();
|
||||
|
||||
const direntPath = useMemo(() => getDirentPath(dirent, path), [dirent, path]);
|
||||
const tagListTitleID = useMemo(() => `detail-list-view-tags-${uuidV4()}`, []);
|
||||
@@ -77,10 +77,6 @@ const FileDetails = React.memo(({ repoID, repoInfo, dirent, path, direntDetail,
|
||||
onFileTagChanged(dirent, direntPath);
|
||||
}, [dirent, direntPath, onFileTagChanged]);
|
||||
|
||||
const updateRecord = useCallback((record) => {
|
||||
setRecord(record);
|
||||
}, []);
|
||||
|
||||
const dom = (
|
||||
<>
|
||||
<DetailItem field={sizeField} className="sf-metadata-property-detail-formatter">
|
||||
@@ -116,8 +112,8 @@ const FileDetails = React.memo(({ repoID, repoInfo, dirent, path, direntDetail,
|
||||
</div>
|
||||
</DetailItem>
|
||||
)}
|
||||
{window.app.pageOptions.enableMetadataManagement && enableMetadata && (
|
||||
<MetadataDetails repoID={repoID} filePath={direntPath} repoInfo={repoInfo} direntType="file" updateRecord={updateRecord} />
|
||||
{enableMetadataManagement && enableMetadata && (
|
||||
<MetadataDetails />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
@@ -9,6 +9,9 @@ import { Detail, Header, Body } from '../detail';
|
||||
import DirDetails from './dir-details';
|
||||
import FileDetails from './file-details';
|
||||
import ObjectUtils from '../../../metadata/utils/object-utils';
|
||||
import { MetadataDetailsProvider } from '../../../metadata/hooks';
|
||||
import Settings from '../../../metadata/components/metadata-details/settings';
|
||||
import { getDirentPath } from './utils';
|
||||
|
||||
import './index.css';
|
||||
|
||||
@@ -95,38 +98,59 @@ class DirentDetails extends React.Component {
|
||||
|
||||
render() {
|
||||
const { dirent, direntDetail } = this.state;
|
||||
const { repoID, path, fileTags } = this.props;
|
||||
const { repoID, fileTags } = this.props;
|
||||
|
||||
if (!dirent || !direntDetail) {
|
||||
return (
|
||||
<Detail>
|
||||
<Header title={dirent?.name || ''} icon={Utils.getDirentIcon(dirent, true)} onClose={this.props.onClose} />
|
||||
<Body>
|
||||
{this.renderImage()}
|
||||
</Body>
|
||||
</Detail>
|
||||
);
|
||||
}
|
||||
|
||||
let path = this.props.path;
|
||||
if (dirent?.type !== 'file') {
|
||||
path = this.props.dirent ? Utils.joinPath(path, dirent.name) : path;
|
||||
}
|
||||
|
||||
return (
|
||||
<Detail>
|
||||
<Header title={dirent?.name || ''} icon={Utils.getDirentIcon(dirent, true)} onClose={this.props.onClose} />
|
||||
<Body>
|
||||
{this.renderImage()}
|
||||
{dirent && direntDetail && (
|
||||
<div className="detail-content">
|
||||
{dirent.type !== 'file' ?
|
||||
<DirDetails
|
||||
repoID={repoID}
|
||||
repoInfo={this.props.currentRepoInfo}
|
||||
dirent={dirent}
|
||||
direntDetail={direntDetail}
|
||||
path={this.props.dirent ? Utils.joinPath(path, dirent.name) : path}
|
||||
/>
|
||||
:
|
||||
<FileDetails
|
||||
repoID={repoID}
|
||||
repoInfo={this.props.currentRepoInfo}
|
||||
dirent={dirent}
|
||||
path={path}
|
||||
direntDetail={direntDetail}
|
||||
repoTags={this.props.repoTags}
|
||||
fileTagList={dirent ? dirent.file_tags : fileTags}
|
||||
onFileTagChanged={this.props.onFileTagChanged}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</Body>
|
||||
</Detail>
|
||||
<MetadataDetailsProvider
|
||||
repoID={repoID}
|
||||
repoInfo={this.props.currentRepoInfo}
|
||||
path={getDirentPath(dirent, path)}
|
||||
dirent={dirent}
|
||||
direntDetail={direntDetail}
|
||||
direntType={dirent?.type !== 'file' ? 'dir' : 'file'}
|
||||
>
|
||||
<Detail>
|
||||
<Header title={dirent?.name || ''} icon={Utils.getDirentIcon(dirent, true)} onClose={this.props.onClose} >
|
||||
<Settings />
|
||||
</Header>
|
||||
<Body>
|
||||
{this.renderImage()}
|
||||
{dirent && direntDetail && (
|
||||
<div className="detail-content">
|
||||
{dirent.type !== 'file' ? (
|
||||
<DirDetails direntDetail={direntDetail} />
|
||||
) : (
|
||||
<FileDetails
|
||||
repoID={repoID}
|
||||
dirent={dirent}
|
||||
path={path}
|
||||
direntDetail={direntDetail}
|
||||
repoTags={this.props.repoTags}
|
||||
fileTagList={dirent ? dirent.file_tags : fileTags}
|
||||
onFileTagChanged={this.props.onFileTagChanged}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Body>
|
||||
</Detail>
|
||||
</MetadataDetailsProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@ import { Utils } from '../../../utils/utils';
|
||||
|
||||
export const getDirentPath = (dirent, path) => {
|
||||
if (Utils.isMarkdownFile(path)) return path; // column mode: view file
|
||||
return Utils.joinPath(path, dirent.name);
|
||||
return Utils.joinPath(path, dirent?.name);
|
||||
};
|
||||
|
||||
export const getFileParent = (dirent, path) => {
|
||||
|
@@ -8,7 +8,7 @@ import { Utils } from '../../../utils/utils';
|
||||
import { MetadataDetails } from '../../../metadata';
|
||||
import { useMetadataStatus } from '../../../hooks';
|
||||
|
||||
const FileDetails = ({ repoID, repoInfo, path, direntDetail }) => {
|
||||
const FileDetails = ({ direntDetail }) => {
|
||||
const { enableMetadata } = useMetadataStatus();
|
||||
|
||||
const sizeField = useMemo(() => ({ type: 'size', name: gettext('Size') }), []);
|
||||
@@ -36,16 +36,13 @@ const FileDetails = ({ repoID, repoInfo, path, direntDetail }) => {
|
||||
<Formatter field={lastModifiedTimeField} value={direntDetail.last_modified}/>
|
||||
</DetailItem>
|
||||
{enableMetadata && (
|
||||
<MetadataDetails repoID={repoID} filePath={path} repoInfo={repoInfo} direntType="file" />
|
||||
<MetadataDetails />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
FileDetails.propTypes = {
|
||||
repoID: PropTypes.string,
|
||||
repoInfo: PropTypes.object,
|
||||
path: PropTypes.string,
|
||||
direntDetail: PropTypes.object,
|
||||
};
|
||||
|
||||
|
@@ -7,6 +7,8 @@ import toaster from '../../toast';
|
||||
import { Header, Body } from '../detail';
|
||||
import FileDetails from './file-details';
|
||||
import { MetadataContext } from '../../../metadata';
|
||||
import { MetadataDetailsProvider } from '../../../metadata/hooks';
|
||||
import Settings from '../../../metadata/components/metadata-details/settings';
|
||||
|
||||
import './index.css';
|
||||
|
||||
@@ -36,27 +38,33 @@ const EmbeddedFileDetails = ({ repoID, repoInfo, dirent, path, onClose, width =
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('cur-view-detail', className, {
|
||||
'cur-view-detail-small': width < 400,
|
||||
'cur-view-detail-large': width > 400
|
||||
})}
|
||||
style={{ width }}
|
||||
<MetadataDetailsProvider
|
||||
repoID={repoID}
|
||||
repoInfo={repoInfo}
|
||||
path={path}
|
||||
dirent={dirent}
|
||||
direntDetail={direntDetail}
|
||||
direntType="file"
|
||||
>
|
||||
<Header title={dirent?.name || ''} icon={Utils.getDirentIcon(dirent, true)} onClose={onClose} component={headerComponent} />
|
||||
<Body>
|
||||
{dirent && direntDetail && (
|
||||
<div className="detail-content">
|
||||
<FileDetails
|
||||
repoID={repoID}
|
||||
repoInfo={repoInfo}
|
||||
path={path}
|
||||
direntDetail={direntDetail}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Body>
|
||||
</div>
|
||||
<div
|
||||
className={classnames('cur-view-detail', className, {
|
||||
'cur-view-detail-small': width < 400,
|
||||
'cur-view-detail-large': width > 400
|
||||
})}
|
||||
style={{ width }}
|
||||
>
|
||||
<Header title={dirent?.name || ''} icon={Utils.getDirentIcon(dirent, true)} onClose={onClose} component={headerComponent} >
|
||||
<Settings />
|
||||
</Header>
|
||||
<Body>
|
||||
{dirent && direntDetail && (
|
||||
<div className="detail-content">
|
||||
<FileDetails direntDetail={direntDetail} />
|
||||
</div>
|
||||
)}
|
||||
</Body>
|
||||
</div>
|
||||
</MetadataDetailsProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -31,13 +31,9 @@ const Detail = React.memo(({ repoID, path, dirent, currentRepoInfo, repoTags, fi
|
||||
}
|
||||
|
||||
if (path === '/' && !dirent) {
|
||||
return (
|
||||
<LibDetail
|
||||
currentRepoInfo={currentRepoInfo}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
return (<LibDetail currentRepoInfo={currentRepoInfo} onClose={onClose} />);
|
||||
}
|
||||
|
||||
return (
|
||||
<DirentDetail
|
||||
repoID={repoID}
|
||||
|
@@ -31,7 +31,7 @@ const propTypes = {
|
||||
|
||||
const { isStarred, isLocked, lockedByMe,
|
||||
repoID, filePath, filePerm, enableWatermark, userNickName,
|
||||
fileName, repoEncrypted
|
||||
fileName, repoEncrypted, isRepoAdmin
|
||||
} = window.app.pageOptions;
|
||||
|
||||
class FileView extends React.Component {
|
||||
@@ -116,7 +116,8 @@ class FileView extends React.Component {
|
||||
const { isDetailsPanelOpen, isHeaderShown } = this.state;
|
||||
const repoInfo = {
|
||||
permission: filePerm,
|
||||
encrypted: repoEncrypted
|
||||
encrypted: repoEncrypted,
|
||||
is_admin: isRepoAdmin,
|
||||
};
|
||||
return (
|
||||
<I18nextProvider i18n={ i18n }>
|
||||
|
Reference in New Issue
Block a user