Skip to content

Commit

Permalink
add lightbox sidepanel
Browse files Browse the repository at this point in the history
  • Loading branch information
zhouwenxuan authored and zhouwenxuan committed Jan 14, 2025
1 parent 61215e4 commit 1c7b1bf
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 8 deletions.
1 change: 0 additions & 1 deletion frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"@emoji-mart/data": "^1.2.1",
"@emoji-mart/react": "^1.1.1",
"@gatsbyjs/reach-router": "1.3.9",
"@seafile/react-image-lightbox": "3.0.4",
"@seafile/resumablejs": "1.1.16",
"@seafile/sdoc-editor": "1.0.203",
"@seafile/seafile-calendar": "0.0.28",
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/assets/icons/left_arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions frontend/src/assets/icons/right_arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
import React, { useCallback } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import { gettext } from '../../../utils/constants';
import Lightbox from '@seafile/react-image-lightbox';
import { useMetadataAIOperations } from '../../hooks/metadata-ai-operation';
import { SYSTEM_FOLDERS } from '../../constants';
import { useMetadataAIOperations } from '../../../hooks/metadata-ai-operation';
import { SYSTEM_FOLDERS } from '../../../constants';
import { Utils } from '../../../utils/utils';
import { Dirent } from '../../../models';
import { seafileAPI } from '../../../utils/seafile-api';
import SidePanel from './side-panel';

import '@seafile/react-image-lightbox/style.css';

const ImageDialog = ({ enableRotate: oldEnableRotate, imageItems, imageIndex, closeImagePopup, moveToPrevImage, moveToNextImage, onDeleteImage, onRotateImage }) => {
const ImageDialog = ({ repoID, repoInfo, enableRotate: oldEnableRotate, imageItems, imageIndex, closeImagePopup, moveToPrevImage, moveToNextImage, onDeleteImage, onRotateImage, onFileTagChanged }) => {
const [direntDetail, setDirentDetail] = useState(null);
const { enableOCR, enableMetadata, canModify, onOCR: onOCRAPI, OCRSuccessCallBack } = useMetadataAIOperations();

useEffect(() => {
const getDirentDetail = async (repoID, path) => {
try {
const res = await seafileAPI.getFileInfo(repoID, path);
return res.data;
} catch (error) {
return null;
}
};

const path = Utils.joinPath(imageItems[imageIndex].parentDir, imageItems[imageIndex].name);
getDirentDetail(repoID, path).then(res => {
setDirentDetail(res);
});
}, [imageIndex, imageItems, repoID]);

const downloadImage = useCallback((url) => {
location.href = url;
}, []);
Expand Down Expand Up @@ -38,6 +59,13 @@ const ImageDialog = ({ enableRotate: oldEnableRotate, imageItems, imageIndex, cl
onOCR = () => onOCRAPI({ parentDir: mainImg.parentDir, fileName: mainImg.name }, { success_callback: OCRSuccessCallBack });
}

const renderSidePanel = () => {
const dirent = new Dirent({ name, type: 'file' });
const path = Utils.joinPath(mainImg.parentDir, mainImg.name);

return <SidePanel repoID={repoID} repoInfo={repoInfo} path={path} dirent={dirent} direntDetail={direntDetail} onFileTagChanged={onFileTagChanged} />;
};

return (
<Lightbox
wrapperClassName='custom-image-previewer'
Expand All @@ -63,11 +91,13 @@ const ImageDialog = ({ enableRotate: oldEnableRotate, imageItems, imageIndex, cl
onRotateImage={(onRotateImage && enableRotate) ? (angle) => onRotateImage(imageIndex, angle) : null}
onOCR={onOCR}
OCRLabel={gettext('OCR')}
onRenderSidePanel={renderSidePanel}
/>
);
};

ImageDialog.propTypes = {
repoID: PropTypes.string,
imageItems: PropTypes.array.isRequired,
imageIndex: PropTypes.number.isRequired,
closeImagePopup: PropTypes.func.isRequired,
Expand All @@ -76,6 +106,7 @@ ImageDialog.propTypes = {
onDeleteImage: PropTypes.func,
onRotateImage: PropTypes.func,
enableRotate: PropTypes.bool,
onFileTagChanged: PropTypes.func,
};

ImageDialog.defaultProps = {
Expand Down
89 changes: 89 additions & 0 deletions frontend/src/components/dialog/image-dialog/side-panel/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
.lightbox-side-panel {
display: flex;
flex-direction: column;
position: absolute;
top: 50px;
right: 0;
width: 10px;
height: calc(100% - 100px);
font-size: 1rem;
color: #fff;
background-color: #333;
border: 1px solid #666;
transition: width 0.3s ease;
z-index: 1052;
}

.lightbox-side-panel.expanded {
width: 300px;
}

.lightbox-side-panel.collapsed {
width: 10px;
}

.expand-button {
position: absolute;
top: 30px;
left: -40px;
width: 40px;
height: 48px;
background-color: #333;
color: #fff;
border: 1px solid #666;
border-right: none;
border-top-left-radius: 50%;
border-bottom-left-radius: 50%;
margin-right: -1px;
cursor: pointer;
}

.expand-button .sf-metadata-icon {
width: 32px;
height: 32px;
color: #999;
vertical-align: middle;
}

.lightbox-side-panel .detail-header {
width: 100%;
height: fit-content;
display: flex;
padding: 26px 16px;
border: none;
font-size: 1rem;
}

.lightbox-side-panel .detail-header .detail-title .name,
.lightbox-side-panel .file-details-collapse .file-details-collapse-header .file-details-collapse-header-title,
.lightbox-side-panel .dirent-detail-item .dirent-detail-item-name {
color: #fff;
}

.lightbox-side-panel .detail-body {
overflow-y: auto;
padding: 0;
}

.lightbox-side-panel .detail-content {
margin: 0 16px;
}

.lightbox-side-panel .file-details-collapse {
margin-bottom: 20px;
}

.lightbox-side-panel .file-details-collapse .file-details-collapse-header .sf3-font-down {
color: #999;
}

.lightbox-side-panel .file-details-collapse .file-details-collapse-header .file-details-collapse-header-operation:hover,
.lightbox-side-panel .dirent-detail-item .dirent-detail-item-value:hover {
background-color: #666;
}

.lightbox-side-panel .lightbox-side-panel-divider {
height: 1px;
background-color: #999;
margin-bottom: 20px;
}
66 changes: 66 additions & 0 deletions frontend/src/components/dialog/image-dialog/side-panel/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useEffect, useState } from 'react';
import { MetadataDetailsProvider } from '../../../../metadata';
import Title from '../../../dirent-detail/detail/header/title';
import { Utils } from '../../../../utils/utils';
import PreviewDetails from '../../../dirent-detail/dirent-details/preview-details';

import './index.css';
import FileDetails from '../../../dirent-detail/dirent-details/file-details';
import { Detail, Body } from '../../../dirent-detail/detail';
import { Icon } from '@seafile/sf-metadata-ui-component';

const SidePanel = ({ repoID, repoInfo, path, dirent, direntDetail, onFileTagChanged }) => {
const [expanded, setExpanded] = useState(false);

const handleExpandClick = () => {
setExpanded(!expanded);
};

useEffect(() => {
const element = document.querySelector('.sf-metadata-long-text-editor-dialog');
if (element) {
element.style.zIndex = 1052;
}
});

return (
<MetadataDetailsProvider
repoID={repoID}
repoInfo={repoInfo}
path={path}
dirent={dirent}
direntDetail={direntDetail}
direntType={dirent?.type !== 'file' ? 'dir' : 'file'}
>
<div className={`lightbox-side-panel ${expanded ? 'expanded' : 'collapsed'}`}>
<button className="expand-button" onClick={handleExpandClick}>
{expanded ? (
<Icon iconName="right_arrow" />
) : (
<Icon iconName="left_arrow" />
)}
</button>
{expanded && (
<>
<div className="detail-header">
<Title icon={Utils.getFileIconUrl(dirent.name)} iconSize={32} title={dirent.name} />
</div>
<Body>
<div className="detail-content">
<FileDetails
repoID={repoID}
repoInfo={repoInfo}
dirent={dirent}
path={path}
direntDetail={direntDetail}
/>
</div>
</Body>
</>
)}
</div>
</MetadataDetailsProvider>
);
};

export default SidePanel;
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { Formatter } from '@seafile/sf-metadata-ui-component';
import DetailItem from '../../detail-item';
import { CellType } from '../../../../metadata/constants';
import { gettext } from '../../../../utils/constants';
import { Utils } from '../../../../utils/utils';
import { MetadataDetails } from '../../../../metadata';
import ObjectUtils from '../../../../metadata/utils/object-utils';
import Collapse from '../file-details/collapse';
import { useMetadataStatus } from '../../../../hooks';

import './index.css';

const PreviewDetails = React.memo(({ dirent, direntDetail }) => {
const { enableMetadataManagement, enableMetadata } = useMetadataStatus();

const sizeField = useMemo(() => ({ type: 'size', name: gettext('Size') }), []);
const lastModifierField = useMemo(() => ({ type: CellType.LAST_MODIFIER, name: gettext('Last modifier') }), []);
const lastModifiedTimeField = useMemo(() => ({ type: CellType.MTIME, name: gettext('Last modified time') }), []);

const dom = (
<>
<DetailItem field={sizeField} className="sf-metadata-property-detail-formatter">
<Formatter field={sizeField} value={Utils.bytesToSize(direntDetail.size)} />
</DetailItem>
<DetailItem field={lastModifierField} className="sf-metadata-property-detail-formatter">
<Formatter
field={lastModifierField}
value={direntDetail.last_modifier_email}
collaborators={[{
name: direntDetail.last_modifier_name,
contact_email: direntDetail.last_modifier_contact_email,
email: direntDetail.last_modifier_email,
avatar_url: direntDetail.last_modifier_avatar,
}]}
/>
</DetailItem >
<DetailItem field={lastModifiedTimeField} className="sf-metadata-property-detail-formatter">
<Formatter field={lastModifiedTimeField} value={direntDetail.last_modified}/>
</DetailItem>
{enableMetadataManagement && enableMetadata && (
<MetadataDetails />
)}
</>
);

let component = dom;
if (Utils.imageCheck(dirent.name) || Utils.videoCheck(dirent.name)) {

component = (
<>
<Collapse title={gettext('General information')}>
{dom}
</Collapse>
<div className="lightbox-side-panel-divider"></div>
</>
);
}

return (
<>
{component}
</>
);
}, (props, nextProps) => {
const { repoID, repoInfo, dirent, path, direntDetail } = props;
const isChanged = (
repoID !== nextProps.repoID ||
path !== nextProps.path ||
!ObjectUtils.isSameObject(repoInfo, nextProps.repoInfo) ||
!ObjectUtils.isSameObject(dirent, nextProps.dirent) ||
!ObjectUtils.isSameObject(direntDetail, nextProps.direntDetail)
);
return !isChanged;
});

PreviewDetails.propTypes = {
repoID: PropTypes.string,
repoInfo: PropTypes.object,
dirent: PropTypes.object,
path: PropTypes.string,
direntDetail: PropTypes.object,
};

export default PreviewDetails;
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ 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 ImageDialog from '../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';
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/components/dirent-list-view/dirent-list-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,8 @@ class DirentListView extends React.Component {
{this.state.isImagePopupOpen && (
<ModalPortal>
<ImageDialog
repoID={this.props.repoID}
repoInfo={this.props.currentRepoInfo}
imageItems={this.state.imageItems}
imageIndex={this.state.imageIndex}
closeImagePopup={this.closeImagePopup}
Expand All @@ -857,6 +859,7 @@ class DirentListView extends React.Component {
onDeleteImage={this.deleteImage}
onRotateImage={this.rotateImage}
enableRotate={!repoEncrypted}
onFileTagChanged={this.props.onFileTagChanged}
/>
</ModalPortal>
)}
Expand Down

0 comments on commit 1c7b1bf

Please sign in to comment.