mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-16 15:19:06 +00:00
refactor: section (#6224)
* refactor: section * feat: update code --------- Co-authored-by: 杨国璇 <ygx@Hello-word.local>
This commit is contained in:
@@ -10,10 +10,10 @@ import CreateFolder from '../../components/dialog/create-folder-dialog';
|
||||
import CreateFile from '../../components/dialog/create-file-dialog';
|
||||
import ImageDialog from '../../components/dialog/image-dialog';
|
||||
import { siteRoot, thumbnailSizeForOriginal } from '../../utils/constants';
|
||||
import seahubMetadataAPI from '../metadata-manage/seahub-metadata-api';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import toaster from '../../components/toast';
|
||||
import TreeSection from '../../components/tree-section';
|
||||
import DirViews from './dir-views';
|
||||
|
||||
const propTypes = {
|
||||
currentPath: PropTypes.string.isRequired,
|
||||
@@ -102,22 +102,6 @@ class DirColumnNav extends React.Component {
|
||||
case 'Open in New Tab':
|
||||
this.onOpenFile(node);
|
||||
break;
|
||||
case 'Enable Metadata':
|
||||
if (confirm(gettext('Enable metadata management?'))){
|
||||
seahubMetadataAPI.enableMetadataManagement(this.props.repoID).catch((error) => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'Disable Metadata':
|
||||
if (confirm(gettext('Disable metadata management?'))){
|
||||
seahubMetadataAPI.disableMetadataManagement(this.props.repoID).catch((error) => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -261,32 +245,41 @@ class DirColumnNav extends React.Component {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
renderContent = () => {
|
||||
if (this.props.isTreeDataLoading) return (<Loading/>);
|
||||
return (
|
||||
<>
|
||||
<TreeSection title={gettext('Files')}>
|
||||
<TreeView
|
||||
userPerm={this.props.userPerm}
|
||||
isNodeMenuShow={this.isNodeMenuShow}
|
||||
treeData={this.props.treeData}
|
||||
currentPath={this.props.currentPath}
|
||||
onNodeClick={this.onNodeClick}
|
||||
onNodeExpanded={this.props.onNodeExpanded}
|
||||
onNodeCollapse={this.props.onNodeCollapse}
|
||||
onMenuItemClick={this.onMenuItemClick}
|
||||
onFreezedItem={this.onFreezedItem}
|
||||
onUnFreezedItem={this.onUnFreezedItem}
|
||||
onItemMove={this.props.onItemMove}
|
||||
currentRepoInfo={this.props.currentRepoInfo}
|
||||
selectedDirentList={this.props.selectedDirentList}
|
||||
onItemsMove={this.props.onItemsMove}
|
||||
repoID={this.props.repoID}
|
||||
/>
|
||||
</TreeSection>
|
||||
<DirViews repoID={this.props.repoID} userPerm={this.props.userPerm} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
let flex = this.props.navRate ? '0 0 ' + this.props.navRate * 100 + '%' : '0 0 25%';
|
||||
const select = this.props.inResizing ? 'none' : '';
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="dir-content-nav" role="navigation" style={{flex: (flex), userSelect: select}} onScroll={this.stopTreeScrollPropagation}>
|
||||
{this.props.isTreeDataLoading ?
|
||||
(<Loading/>) :
|
||||
(<TreeView
|
||||
userPerm={this.props.userPerm}
|
||||
isNodeMenuShow={this.isNodeMenuShow}
|
||||
treeData={this.props.treeData}
|
||||
currentPath={this.props.currentPath}
|
||||
onNodeClick={this.onNodeClick}
|
||||
onNodeExpanded={this.props.onNodeExpanded}
|
||||
onNodeCollapse={this.props.onNodeCollapse}
|
||||
onMenuItemClick={this.onMenuItemClick}
|
||||
onFreezedItem={this.onFreezedItem}
|
||||
onUnFreezedItem={this.onUnFreezedItem}
|
||||
onItemMove={this.props.onItemMove}
|
||||
currentRepoInfo={this.props.currentRepoInfo}
|
||||
selectedDirentList={this.props.selectedDirentList}
|
||||
onItemsMove={this.props.onItemsMove}
|
||||
repoID={this.props.repoID}
|
||||
/>)
|
||||
}
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
{this.state.isAddFolderDialogShow && (
|
||||
<ModalPortal>
|
||||
|
75
frontend/src/components/dir-view-mode/dir-views/index.js
Normal file
75
frontend/src/components/dir-view-mode/dir-views/index.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import { Utils } from '../../../utils/utils';
|
||||
import TreeSection from '../../tree-section';
|
||||
import MetadataStatusManagementDialog from '../../metadata-manage/metadata-status-manage-dialog';
|
||||
import metadataManagerAPI from '../../metadata-manage/api';
|
||||
import toaster from '../../toast';
|
||||
import MetadataViews from '../../metadata-manage/metadata-views';
|
||||
|
||||
const DirViews = ({ userPerm, repoID }) => {
|
||||
const enableMetadataManagement = useMemo(() => {
|
||||
return window.app.pageOptions.enableMetadataManagement;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [window.app.pageOptions.enableMetadataManagement]);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showMetadataStatusManagementDialog, setShowMetadataStatusManagementDialog] = useState(false);
|
||||
const [metadataStatus, setMetadataStatus] = useState(false);
|
||||
const moreOperations = useMemo(() => {
|
||||
if (!enableMetadataManagement) return [];
|
||||
if (userPerm !== 'rw' && userPerm !== 'admin') return [];
|
||||
return [
|
||||
{ key: 'extended-properties', value: gettext('Extended properties') }
|
||||
];
|
||||
}, [enableMetadataManagement, userPerm]);
|
||||
|
||||
useEffect(() => {
|
||||
const repoMetadataManagementEnabledStatusRes = metadataManagerAPI.getRepoMetadataManagementEnabledStatus(repoID);
|
||||
Promise.all([repoMetadataManagementEnabledStatusRes]).then(results => {
|
||||
const [repoMetadataManagementEnabledStatusRes] = results;
|
||||
setMetadataStatus(repoMetadataManagementEnabledStatusRes.data.enabled);
|
||||
setLoading(false);
|
||||
}).catch(error => {
|
||||
const errorMsg = Utils.getErrorMsg(error, true);
|
||||
toaster.danger(errorMsg);
|
||||
setLoading(false);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const moreOperationClick = useCallback((operationKey) => {
|
||||
if (operationKey === 'extended-properties') {
|
||||
setShowMetadataStatusManagementDialog(true);
|
||||
return;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const closeMetadataManagementDialog = useCallback(() => {
|
||||
setShowMetadataStatusManagementDialog(false);
|
||||
}, []);
|
||||
|
||||
const toggleMetadataStatus = useCallback((value) => {
|
||||
if (metadataStatus === value) return;
|
||||
setMetadataStatus(value);
|
||||
}, [metadataStatus]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TreeSection title={gettext('Views')} moreKey={{ name: 'views' }} moreOperations={moreOperations} moreOperationClick={moreOperationClick}>
|
||||
{!loading && metadataStatus && (<MetadataViews repoID={repoID} />)}
|
||||
</TreeSection>
|
||||
{showMetadataStatusManagementDialog && (
|
||||
<MetadataStatusManagementDialog value={metadataStatus} repoID={repoID} toggle={closeMetadataManagementDialog} submit={toggleMetadataStatus} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
DirViews.propTypes = {
|
||||
userPerm: PropTypes.string,
|
||||
repoID: PropTypes.string,
|
||||
};
|
||||
|
||||
export default DirViews;
|
@@ -2,7 +2,7 @@ import axios from 'axios';
|
||||
import cookie from 'react-cookies';
|
||||
import { siteRoot } from '../../utils/constants';
|
||||
|
||||
class SeahubMetadataAPI {
|
||||
class MetadataManagerAPI {
|
||||
init({ server, username, password, token }) {
|
||||
this.server = server;
|
||||
this.username = username;
|
||||
@@ -43,17 +43,17 @@ class SeahubMetadataAPI {
|
||||
}
|
||||
}
|
||||
|
||||
getMetadataManagementEnabledStatus(repoID) {
|
||||
getRepoMetadataManagementEnabledStatus(repoID) {
|
||||
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/';
|
||||
return this.req.get(url);
|
||||
}
|
||||
|
||||
enableMetadataManagement(repoID) {
|
||||
openRepoMetadataManagement(repoID) {
|
||||
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/';
|
||||
return this.req.put(url);
|
||||
}
|
||||
|
||||
disableMetadataManagement(repoID) {
|
||||
closeRepoMetadataManagement(repoID) {
|
||||
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/';
|
||||
return this.req.delete(url);
|
||||
}
|
||||
@@ -91,8 +91,8 @@ class SeahubMetadataAPI {
|
||||
}
|
||||
}
|
||||
|
||||
const seahubMetadataAPI = new SeahubMetadataAPI();
|
||||
const metadataManagerAPI = new MetadataManagerAPI();
|
||||
const xcsrfHeaders = cookie.load('sfcsrftoken');
|
||||
seahubMetadataAPI.initForSeahubUsage({ siteRoot, xcsrfHeaders });
|
||||
metadataManagerAPI.initForSeahubUsage({ siteRoot, xcsrfHeaders });
|
||||
|
||||
export default seahubMetadataAPI;
|
||||
export default metadataManagerAPI;
|
@@ -1,129 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import toaster from '../toast';
|
||||
import seahubMetadataAPI from './seahub-metadata-api';
|
||||
import { siteRoot } from '../../utils/constants';
|
||||
import { hideMenu, showMenu } from '../context-menu/actions';
|
||||
import TextTranslation from '../../utils/text-translation';
|
||||
|
||||
const propTypes = {
|
||||
repoID: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
class MetadataManageView extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isHighlight: false,
|
||||
menuList: []
|
||||
};
|
||||
}
|
||||
|
||||
onMouseEnter = () => {
|
||||
this.setState({
|
||||
isHighlight: true,
|
||||
});
|
||||
};
|
||||
|
||||
onMouseOver = () => {
|
||||
this.setState({
|
||||
isHighlight: true,
|
||||
});
|
||||
};
|
||||
|
||||
onMouseLeave = () => {
|
||||
this.setState({
|
||||
isHighlight: false,
|
||||
});
|
||||
};
|
||||
|
||||
onItemMouseDown = (event) => {
|
||||
event.stopPropagation();
|
||||
if (event.button === 2) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
getMenuList = () => {
|
||||
let { ENABLE_METADATA, DISABLE_METADATA } = TextTranslation;
|
||||
seahubMetadataAPI.getMetadataManagementEnabledStatus(this.props.repoID).then((res) => {
|
||||
if (res.data.enabled){
|
||||
this.setState({ menuList: [DISABLE_METADATA] });
|
||||
} else {
|
||||
this.setState({ menuList: [ENABLE_METADATA] });
|
||||
}
|
||||
}).catch((error) => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
this.setState({ menuList: [] });
|
||||
});
|
||||
};
|
||||
|
||||
onItemContextMenu = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let x = event.clientX || (event.touches && event.touches[0].pageX);
|
||||
let y = event.clientY || (event.touches && event.touches[0].pageY);
|
||||
|
||||
hideMenu();
|
||||
|
||||
this.getMenuList();
|
||||
|
||||
let showMenuConfig = {
|
||||
id: 'tree-node-contextmenu',
|
||||
position: { x, y },
|
||||
target: event.target,
|
||||
menuList: this.state.menuList,
|
||||
};
|
||||
|
||||
showMenu(showMenuConfig);
|
||||
};
|
||||
|
||||
onClick = () => {
|
||||
seahubMetadataAPI.getMetadataManagementEnabledStatus(this.props.repoID).then((res) => {
|
||||
if (res.data.enabled){
|
||||
this.viewMetadata();
|
||||
} else {
|
||||
let message = gettext('This repo has not enabled metadata management, please enabled firstly');
|
||||
toaster.notify(message);
|
||||
}
|
||||
}).catch((error) => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
};
|
||||
|
||||
viewMetadata = () => {
|
||||
let server = siteRoot.substring(0, siteRoot.length-1);
|
||||
window.open(server + '/repos/' + this.props.repoID + '/metadata/table-view/', '_blank');
|
||||
};
|
||||
|
||||
render() {
|
||||
let hlClass = this.state.isHighlight ? 'tree-node-inner-hover ' : '';
|
||||
return (
|
||||
<div className="tree-node">
|
||||
<div
|
||||
className={`tree-node-inner text-nowrap ${hlClass}`}
|
||||
title="Metadata Views"
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseOver={this.onMouseOver}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
onMouseDown={this.onItemMouseDown}
|
||||
onClick={this.onClick}
|
||||
onContextMenu={this.onItemContextMenu}
|
||||
>
|
||||
<div style={{ paddingLeft: 44.8 }}>{gettext('Metadata Views')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MetadataManageView.propTypes = propTypes;
|
||||
|
||||
export default MetadataManageView;
|
@@ -1,128 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import toaster from '../toast';
|
||||
import seahubMetadataAPI from './seahub-metadata-api';
|
||||
import { hideMenu, showMenu } from '../context-menu/actions';
|
||||
import TextTranslation from '../../utils/text-translation';
|
||||
|
||||
const propTypes = {
|
||||
repoID: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
class MetadataManage extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isHighlight: false,
|
||||
menuList: []
|
||||
};
|
||||
}
|
||||
|
||||
onMouseEnter = () => {
|
||||
this.setState({
|
||||
isHighlight: true,
|
||||
});
|
||||
};
|
||||
|
||||
onMouseOver = () => {
|
||||
this.setState({
|
||||
isHighlight: true,
|
||||
});
|
||||
};
|
||||
|
||||
onMouseLeave = () => {
|
||||
this.setState({
|
||||
isHighlight: false,
|
||||
});
|
||||
};
|
||||
|
||||
onItemMouseDown = (event) => {
|
||||
event.stopPropagation();
|
||||
if (event.button === 2) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
getMenuList = () => {
|
||||
let { ENABLE_METADATA, DISABLE_METADATA } = TextTranslation;
|
||||
seahubMetadataAPI.getMetadataManagementEnabledStatus(this.props.repoID).then((res) => {
|
||||
if (res.data.enabled){
|
||||
this.setState({ menuList: [DISABLE_METADATA] });
|
||||
} else {
|
||||
this.setState({ menuList: [ENABLE_METADATA] });
|
||||
}
|
||||
}).catch((error) => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
this.setState({ menuList: [] });
|
||||
});
|
||||
};
|
||||
|
||||
onItemContextMenu = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let x = event.clientX || (event.touches && event.touches[0].pageX);
|
||||
let y = event.clientY || (event.touches && event.touches[0].pageY);
|
||||
|
||||
hideMenu();
|
||||
|
||||
this.getMenuList();
|
||||
|
||||
let showMenuConfig = {
|
||||
id: 'tree-node-contextmenu',
|
||||
position: { x, y },
|
||||
target: event.target,
|
||||
menuList: this.state.menuList,
|
||||
};
|
||||
|
||||
showMenu(showMenuConfig);
|
||||
};
|
||||
|
||||
onClick = () => {
|
||||
seahubMetadataAPI.getMetadataManagementEnabledStatus(this.props.repoID).then((res) => {
|
||||
if (!res.data.enabled && confirm(gettext('Enable metadata management?'))){
|
||||
seahubMetadataAPI.enableMetadataManagement(this.props.repoID).catch((error) => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
}).catch((error) => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
let hlClass = this.state.isHighlight ? 'tree-node-inner-hover ' : '';
|
||||
return (
|
||||
<div className="tree-node">
|
||||
<div
|
||||
className={`tree-node-inner text-nowrap ${hlClass}`}
|
||||
title="Metadata"
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseOver={this.onMouseOver}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
onMouseDown={this.onItemMouseDown}
|
||||
onClick={this.onClick}
|
||||
onContextMenu={this.onItemContextMenu}
|
||||
>
|
||||
<div className="tree-node-text">{gettext('Metadata')}
|
||||
<div className="left-icon">
|
||||
<i className="tree-node-icon">
|
||||
<span class="sf2-icon-cog2" aria-hidden="true" />
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MetadataManage.propTypes = propTypes;
|
||||
|
||||
export default MetadataManage;
|
@@ -0,0 +1,11 @@
|
||||
.metadata-status-management-dialog .change-metadata-status-management {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.metadata-status-management-dialog .change-metadata-status-management .custom-switch {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.metadata-status-management-dialog .tip {
|
||||
font-size: 12px;
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import Switch from '../../common/switch';
|
||||
import metadataManagerAPI from '../api';
|
||||
import { Utils } from '../../../utils/utils';
|
||||
import toaster from '../../toast';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const MetadataStatusManagementDialog = ({ value: oldValue, repoID, toggle, submit }) => {
|
||||
const [value, setValue] = useState(oldValue);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
const onToggle = useCallback(() => {
|
||||
if (submitting) return;
|
||||
toggle && toggle();
|
||||
}, [submitting, toggle]);
|
||||
|
||||
const onSubmit = useCallback(() => {
|
||||
setSubmitting(true);
|
||||
const apiName = value ? 'openRepoMetadataManagement' : 'closeRepoMetadataManagement';
|
||||
metadataManagerAPI[apiName](repoID).then(res => {
|
||||
submit(value);
|
||||
toggle();
|
||||
}).catch(error => {
|
||||
const errorMsg = Utils.getErrorMsg(error);
|
||||
toaster.danger(errorMsg);
|
||||
setSubmitting(false);
|
||||
});
|
||||
}, [repoID, value, submit, toggle]);
|
||||
|
||||
const onValueChange = useCallback(() => {
|
||||
const nextValue = !value;
|
||||
setValue(nextValue);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<Modal className="metadata-status-management-dialog" isOpen={true} toggle={onToggle}>
|
||||
<ModalHeader toggle={onToggle}>{gettext('Extended properties management')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<Switch
|
||||
checked={value}
|
||||
disabled={submitting}
|
||||
size="large"
|
||||
textPosition="right"
|
||||
className="change-metadata-status-management w-100"
|
||||
onChange={onValueChange}
|
||||
placeholder={gettext('Enable extended properties' )}
|
||||
/>
|
||||
<div className="tip">
|
||||
{gettext('After enable extended properties for files, you can add different properties to files, like collaborators, file expiring time, file description. You can also create different views for files based extended properties.')}
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="secondary" onClick={onToggle}>{gettext('Cancel')}</Button>
|
||||
<Button color="primary" disabled={oldValue === value || submitting} onClick={onSubmit}>{gettext('Submit')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
MetadataStatusManagementDialog.propTypes = {
|
||||
value: PropTypes.bool,
|
||||
repoID: PropTypes.string.isRequired,
|
||||
toggle: PropTypes.func.isRequired,
|
||||
submit: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default MetadataStatusManagementDialog;
|
@@ -0,0 +1,23 @@
|
||||
.metadata-tree-view .tree-node-inner .left-icon {
|
||||
top: 8px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.metadata-tree-view .tree-node-inner .tree-node-text {
|
||||
padding-left: 28.8px;
|
||||
}
|
||||
|
||||
.metadata-tree-view .tree-node-icon {
|
||||
height: 100%;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.metadata-tree-view .metadata-views-icon {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import { siteRoot } from '../../../utils/constants';
|
||||
import Icon from '../../icon';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const MetadataViews = ({ repoID }) => {
|
||||
const [highlight, setHighlight] = useState(false);
|
||||
|
||||
const onMouseEnter = useCallback(() => {
|
||||
setHighlight(true);
|
||||
}, []);
|
||||
|
||||
const onMouseOver = useCallback(() => {
|
||||
setHighlight(true);
|
||||
}, []);
|
||||
|
||||
const onMouseLeave = useCallback(() => {
|
||||
setHighlight(false);
|
||||
}, []);
|
||||
|
||||
const openView = useCallback(() => {
|
||||
const server = siteRoot.substring(0, siteRoot.length-1);
|
||||
window.open(server + '/repos/' + repoID + '/metadata/table-view/', '_blank');
|
||||
}, [repoID]);
|
||||
|
||||
return (
|
||||
<div className="tree-view tree metadata-tree-view">
|
||||
<div className="tree-node">
|
||||
<div className="children" style={{ paddingLeft: 20 }}>
|
||||
<div
|
||||
className={`tree-node-inner text-nowrap${highlight ? ' tree-node-inner-hover' : ''}`}
|
||||
title={gettext('File extended properties')}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseOver={onMouseOver}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={openView}
|
||||
>
|
||||
<div className="tree-node-text">{gettext('File extended properties')}</div>
|
||||
<div className="left-icon">
|
||||
<div className="tree-node-icon">
|
||||
<Icon symbol="table" className="metadata-views-icon" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
MetadataViews.propTypes = {
|
||||
repoID: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default MetadataViews;
|
96
frontend/src/components/tree-section/index.css
Normal file
96
frontend/src/components/tree-section/index.css
Normal file
@@ -0,0 +1,96 @@
|
||||
.tree-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
padding: 0 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 8px;
|
||||
height: 32px;
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header.tree-section-header-hover,
|
||||
.tree-section .tree-section-header:not(.tree-section-header-hover):hover {
|
||||
background-color: #F0F0F0;
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header .tree-section-header-title {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
cursor: default;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header .tree-section-header-operations {
|
||||
height: 20px;
|
||||
width: auto;
|
||||
flex-shrink: 0;
|
||||
margin-left: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header .tree-section-header-operation {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header .tree-section-header-operation:hover {
|
||||
background-color: #DBDBDB;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header .sf3-font-drop-down {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
transform: scale(.8);
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header .sf3-font-drop-down.icon-rotate-90 {
|
||||
transform: rotate(90deg) scale(.8);
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header-operation .dropdown {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header-operation .dropdown .sf-dropdown-toggle {
|
||||
margin-left: 0;
|
||||
line-height: 1.5;
|
||||
font-size: 16px;
|
||||
color: #999 !important;
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header .tree-section-more-operation {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tree-section .tree-section-header.tree-section-header-hover .tree-section-more-operation,
|
||||
.tree-section .tree-section-header:not(.tree-section-header-hover):hover .tree-section-more-operation {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tree-section .tree-section-body .tree-view {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
86
frontend/src/components/tree-section/index.js
Normal file
86
frontend/src/components/tree-section/index.js
Normal file
@@ -0,0 +1,86 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ItemDropdownMenu from '../dropdown-menu/item-dropdown-menu';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const TreeSection = ({ title, children, moreKey, moreOperations, moreOperationClick }) => {
|
||||
const [showChildren, setShowChildren] = useState(true);
|
||||
const [highlight, setHighlight] = useState(false);
|
||||
const [freeze, setFreeze] = useState(false);
|
||||
|
||||
const validMoreOperations = useMemo(() => {
|
||||
if (!Array.isArray(moreOperations) || moreOperations.length === 0) return [];
|
||||
return moreOperations.filter(operation => operation.key && operation.value);
|
||||
}, [moreOperations]);
|
||||
|
||||
const toggleShowChildren = useCallback(() => {
|
||||
setShowChildren(!showChildren);
|
||||
}, [showChildren]);
|
||||
|
||||
const onMouseEnter = useCallback(() => {
|
||||
if (freeze) return;
|
||||
setHighlight(true);
|
||||
}, [freeze]);
|
||||
|
||||
const onMouseOver = useCallback(() => {
|
||||
if (freeze) return;
|
||||
setHighlight(true);
|
||||
}, [freeze]);
|
||||
|
||||
const onMouseLeave = useCallback(() => {
|
||||
if (freeze) return;
|
||||
setHighlight(false);
|
||||
}, [freeze]);
|
||||
|
||||
const freezeItem = useCallback(() => {
|
||||
setFreeze(true);
|
||||
}, []);
|
||||
|
||||
const unfreezeItem = useCallback(() => {
|
||||
setFreeze(false);
|
||||
setHighlight(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="tree-section">
|
||||
<div className={`tree-section-header${highlight ? ' tree-section-header-hover' : ''}`} onMouseEnter={onMouseEnter} onMouseOver={onMouseOver} onMouseLeave={onMouseLeave}>
|
||||
<div className="tree-section-header-title">{title}</div>
|
||||
<div className="tree-section-header-operations">
|
||||
{validMoreOperations.length > 0 && (
|
||||
<>
|
||||
<div className="tree-section-header-operation tree-section-more-operation">
|
||||
<ItemDropdownMenu
|
||||
item={moreKey}
|
||||
toggleClass="sf3-font sf3-font-more"
|
||||
freezeItem={freezeItem}
|
||||
unfreezeItem={unfreezeItem}
|
||||
getMenuList={() => validMoreOperations}
|
||||
onMenuItemClick={moreOperationClick}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="tree-section-header-operation" onClick={toggleShowChildren}>
|
||||
<i className={`sf3-font sf3-font-drop-down${showChildren ? '' : ' icon-rotate-90'}`}></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{showChildren && (
|
||||
<div className="tree-section-body">
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
TreeSection.propTypes = {
|
||||
title: PropTypes.any.isRequired,
|
||||
moreOperations: PropTypes.array,
|
||||
children: PropTypes.any,
|
||||
moreKey: PropTypes.object,
|
||||
moreOperationClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export default TreeSection;
|
@@ -2,8 +2,6 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TextTranslation from '../../utils/text-translation';
|
||||
import TreeNodeView from './tree-node-view';
|
||||
import MetadataManage from '../metadata-manage/metadata-manage';
|
||||
import MetadataManageView from '../metadata-manage/metadata-manage-view';
|
||||
import ContextMenu from '../context-menu/context-menu';
|
||||
import { hideMenu, showMenu } from '../context-menu/actions';
|
||||
import { Utils } from '../../utils/utils';
|
||||
@@ -344,12 +342,6 @@ class TreeView extends React.Component {
|
||||
onNodeDragLeave={this.onNodeDragLeave}
|
||||
handleContextClick={this.handleContextClick}
|
||||
/>
|
||||
{window.app.pageOptions.enableMetadataManagement && <MetadataManage
|
||||
repoID={this.props.repoID}
|
||||
/>}
|
||||
{window.app.pageOptions.enableMetadataManagement && <MetadataManageView
|
||||
repoID={this.props.repoID}
|
||||
/>}
|
||||
<ContextMenu
|
||||
id={'tree-node-contextmenu'}
|
||||
onMenuItemClick={this.onMenuItemClick}
|
||||
|
@@ -71,7 +71,8 @@
|
||||
|
||||
.tree-node-inner {
|
||||
position: relative;
|
||||
height: 24px;
|
||||
height: 32px;
|
||||
padding: 4px 0;
|
||||
cursor: pointer;
|
||||
line-height: 1.625;
|
||||
}
|
||||
@@ -95,7 +96,7 @@
|
||||
position: absolute;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
top: 0;
|
||||
top: 4px;
|
||||
left: 0;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
@@ -124,7 +125,7 @@
|
||||
|
||||
.tree-node-inner .right-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
top: 4px;
|
||||
right: 0.5rem;
|
||||
bottom: 0;
|
||||
width: 1rem;
|
||||
|
@@ -98,7 +98,8 @@
|
||||
|
||||
.tree-node-inner {
|
||||
position: relative;
|
||||
height: 24px;
|
||||
height: 32px;
|
||||
padding: 4px 0;
|
||||
cursor: pointer;
|
||||
line-height: 1.625;
|
||||
}
|
||||
@@ -123,7 +124,7 @@
|
||||
display:flex;
|
||||
justify-content:center;
|
||||
align-items:center;
|
||||
top: 0;
|
||||
top: 4px;
|
||||
left: 0;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
@@ -148,7 +149,7 @@
|
||||
|
||||
.tree-node-inner .right-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
top: 4px;
|
||||
right: 1rem;
|
||||
bottom: 0;
|
||||
width: 1.5rem;
|
||||
|
@@ -34,8 +34,6 @@ const TextTranslation = {
|
||||
'TAGS': {key: 'Tags', value: gettext('Tags')},
|
||||
'TRASH': {key: 'Trash', value: gettext('Trash')},
|
||||
'ONLYOFFICE_CONVERT': {key: 'Convert with ONLYOFFICE', value: gettext('Convert with ONLYOFFICE')},
|
||||
'ENABLE_METADATA': {key: 'Enable Metadata', value: gettext('Enable Metadata')},
|
||||
'DISABLE_METADATA': {key: 'Disable Metadata', value: gettext('Disable Metadata')}
|
||||
};
|
||||
|
||||
export default TextTranslation;
|
||||
|
Reference in New Issue
Block a user