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

Feature/gallery info side panel (#6876)

* gallery info side panel

* clean up code

* optimize code

* optimize code

* feat: optimize code

* feat: optimize code

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
This commit is contained in:
Aries
2024-10-18 16:01:00 +08:00
committed by GitHub
parent d46423bae7
commit a9c0caff75
18 changed files with 338 additions and 117 deletions

View File

@@ -10,6 +10,7 @@ import ViewModes from '../../components/view-modes';
import ReposSortMenu from '../../components/repos-sort-menu';
import MetadataViewToolBar from '../../metadata/components/view-toolbar';
import { PRIVATE_FILE_TYPE } from '../../constants';
import { DIRENT_DETAIL_MODE } from '../dir-view-mode/constants';
const propTypes = {
repoID: PropTypes.string.isRequired,
@@ -97,10 +98,14 @@ class DirTool extends React.Component {
this.props.sortItems(sortBy, sortOrder);
};
showDirentDetail = () => {
this.props.switchViewMode(DIRENT_DETAIL_MODE);
};
render() {
const menuItems = this.getMenu();
const { isDropdownMenuOpen } = this.state;
const { repoID, currentMode, currentPath, sortBy, sortOrder, viewId } = this.props;
const { repoID, currentMode, currentPath, sortBy, sortOrder, viewId, isCustomPermission } = this.props;
const propertiesText = TextTranslation.PROPERTIES.value;
const isFileExtended = currentPath.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/');
@@ -114,7 +119,7 @@ class DirTool extends React.Component {
if (isFileExtended) {
return (
<div className="dir-tool">
<MetadataViewToolBar viewId={viewId} />
<MetadataViewToolBar viewId={viewId} isCustomPermission={isCustomPermission} showDetail={this.showDirentDetail} />
</div>
);
}
@@ -124,8 +129,8 @@ class DirTool extends React.Component {
<div className="dir-tool d-flex">
<ViewModes currentViewMode={currentMode} switchViewMode={this.props.switchViewMode} />
<ReposSortMenu sortOptions={sortOptions} onSelectSortOption={this.onSelectSortOption}/>
{(!this.props.isCustomPermission) &&
<div className="cur-view-path-btn" onClick={() => this.props.switchViewMode('detail')}>
{(!isCustomPermission) &&
<div className="cur-view-path-btn" onClick={this.showDirentDetail}>
<span className="sf3-font sf3-font-info" aria-label={propertiesText} title={propertiesText}></span>
</div>
}

View File

@@ -1,4 +1,5 @@
export const LIST_MODE = 'list';
export const GRID_MODE = 'grid';
export const DIRENT_DETAIL_MODE = 'detail';
export const METADATA_MODE = 'metadata';
export const FACE_RECOGNITION_MODE = 'person_image';

View File

@@ -80,6 +80,7 @@ const propTypes = {
fullDirentList: PropTypes.array,
onItemsScroll: PropTypes.func.isRequired,
eventBus: PropTypes.object,
updateCurrentDirent: PropTypes.func.isRequired,
};
class DirColumnView extends React.Component {
@@ -202,6 +203,7 @@ class DirColumnView extends React.Component {
viewID={this.props.viewId}
deleteFilesCallback={this.props.deleteFilesCallback}
renameFileCallback={this.props.renameFileCallback}
updateCurrentDirent={this.props.updateCurrentDirent}
/>
}
{currentMode === FACE_RECOGNITION_MODE &&

View File

@@ -2,12 +2,16 @@ import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import LibDetail from './lib-details';
import DirentDetail from './dirent-details';
import ViewDetails from '../../metadata/components/view-details';
import ObjectUtils from '../../metadata/utils/object-utils';
import { MetadataContext } from '../../metadata';
import { PRIVATE_FILE_TYPE } from '../../constants';
const DetailContainer = React.memo(({ repoID, path, dirent, currentRepoInfo, repoTags, fileTags, onClose, onFileTagChanged }) => {
useEffect(() => {
if (path.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES)) return;
// init context
const context = new MetadataContext();
window.sfMetadataContext = context;
@@ -16,7 +20,13 @@ const DetailContainer = React.memo(({ repoID, path, dirent, currentRepoInfo, rep
window.sfMetadataContext.destroy();
delete window['sfMetadataContext'];
};
}, [repoID, currentRepoInfo]);
}, [repoID, currentRepoInfo, path]);
if (path.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES)) {
const viewId = path.split('/').pop();
if (!dirent) return (<ViewDetails viewId={viewId} onClose={onClose} />);
path = dirent.path;
}
if (path === '/' && !dirent) {
return (

View File

@@ -16,6 +16,15 @@
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;

View File

@@ -4,12 +4,14 @@ import Icon from '../../../icon';
import './index.css';
const Header = ({ title, icon, onClose, component = {} }) => {
const Header = ({ title, icon, iconSize = 32, onClose, component = {} }) => {
const { closeIcon } = component;
return (
<div className="detail-header">
<div className="detail-title dirent-title">
<img src={icon} width="32" height="32" alt="" />
<div className="detail-header-icon-container">
<img src={icon} width={iconSize} height={iconSize} alt="" />
</div>
<span className="name ellipsis" title={title}>{title}</span>
</div>
<div className="detail-control" onClick={onClose}>
@@ -22,6 +24,7 @@ const Header = ({ title, icon, onClose, component = {} }) => {
Header.propTypes = {
title: PropTypes.string.isRequired,
icon: PropTypes.string.isRequired,
iconSize: PropTypes.number,
component: PropTypes.object,
onClose: PropTypes.func.isRequired,
};

View File

@@ -42,3 +42,8 @@
.detail-body .sf-metadata-property-detail-tags.tags-empty {
padding: 6.5px 6px;;
}
.detail-body .detail-content.detail-content-empty {
width: 100%;
height: 100%;
}

View File

@@ -0,0 +1,3 @@
.sf-metadata-view-detail .detail-content-empty .empty-tip {
margin: 0;
}

View File

@@ -0,0 +1,39 @@
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { gettext, mediaUrl } from '../../../utils/constants';
import { Detail, Header, Body } from '../../../components/dirent-detail/detail';
import EmptyTip from '../../../components/empty-tip';
import { useMetadata } from '../../hooks';
import { VIEW_TYPE } from '../../constants';
import './index.css';
const ViewDetails = ({ viewId, onClose }) => {
const { viewsMap } = useMetadata();
const view = useMemo(() => viewsMap[viewId], [viewId, viewsMap]);
const icon = useMemo(() => {
const type = view.type;
if (type === VIEW_TYPE.GALLERY) return `${mediaUrl}favicons/gallery.png`;
if (type === VIEW_TYPE.TABLE) return `${mediaUrl}favicons/table.png`;
return `${mediaUrl}img/file/256/file.png`;
}, [view]);
return (
<Detail className="sf-metadata-view-detail">
<Header title={view.name} icon={icon} iconSize={28} onClose={onClose} />
<Body>
<div className="detail-content detail-content-empty">
<EmptyTip text={gettext('There is no information to display.')} />
</div>
</Body>
</Detail>
);
};
ViewDetails.propTypes = {
viewId: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
};
export default ViewDetails;

View File

@@ -0,0 +1,71 @@
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { GalleryGroupBySetter, GallerySliderSetter, FilterSetter, SortSetter } from '../../data-process-setter';
import { PRIVATE_COLUMN_KEY } from '../../../constants';
import { gettext } from '../../../../utils/constants';
const GalleryViewToolbar = ({
readOnly, isCustomPermission, view, collaborators,
modifyFilters, modifySorts, showDetail,
}) => {
const viewType = useMemo(() => view.type, [view]);
const viewColumns = useMemo(() => {
if (!view) return [];
return view.columns;
}, [view]);
const filterColumns = useMemo(() => {
return viewColumns.filter(c => c.key !== PRIVATE_COLUMN_KEY.FILE_TYPE);
}, [viewColumns]);
return (
<>
<div className="sf-metadata-tool-left-operations">
<GalleryGroupBySetter view={view} />
<GallerySliderSetter view={view} />
<FilterSetter
isNeedSubmit={true}
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-filter"
filtersClassName="sf-metadata-filters"
target="sf-metadata-filter-popover"
readOnly={readOnly}
filterConjunction={view.filter_conjunction}
basicFilters={view.basic_filters}
filters={view.filters}
columns={filterColumns}
modifyFilters={modifyFilters}
collaborators={collaborators}
viewType={viewType}
/>
<SortSetter
isNeedSubmit={true}
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-sort"
target="sf-metadata-sort-popover"
readOnly={readOnly}
sorts={view.sorts}
type={viewType}
columns={viewColumns}
modifySorts={modifySorts}
/>
{!isCustomPermission && (
<div className="cur-view-path-btn ml-2" onClick={showDetail}>
<span className="sf3-font sf3-font-info" aria-label={gettext('Properties')} title={gettext('Properties')}></span>
</div>
)}
</div>
<div className="sf-metadata-tool-right-operations"></div>
</>
);
};
GalleryViewToolbar.propTypes = {
readOnly: PropTypes.bool,
isCustomPermission: PropTypes.bool,
view: PropTypes.object.isRequired,
collaborators: PropTypes.array,
modifyFilters: PropTypes.func,
modifySorts: PropTypes.func,
showDetail: PropTypes.func,
};
export default GalleryViewToolbar;

View File

@@ -1,23 +1,15 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { GalleryGroupBySetter, GallerySliderSetter, FilterSetter, GroupbySetter, SortSetter, HideColumnSetter } from '../data-process-setter';
import { EVENT_BUS_TYPE, PRIVATE_COLUMN_KEY, VIEW_TYPE } from '../../constants';
import { EVENT_BUS_TYPE, VIEW_TYPE } from '../../constants';
import TableViewToolbar from './table-view-toolbar';
import GalleryViewToolbar from './gallery-view-toolbar';
import './index.css';
const ViewToolBar = ({ viewId }) => {
const ViewToolBar = ({ viewId, isCustomPermission, showDetail }) => {
const [view, setView] = useState(null);
const [collaborators, setCollaborators] = useState([]);
const viewColumns = useMemo(() => {
if (!view) return [];
return view.columns;
}, [view]);
const filterColumns = useMemo(() => {
return viewColumns.filter(c => c.key !== PRIVATE_COLUMN_KEY.FILE_TYPE);
}, [viewColumns]);
const onHeaderClick = useCallback(() => {
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SELECT_NONE);
}, []);
@@ -67,74 +59,44 @@ const ViewToolBar = ({ viewId }) => {
if (!view) return null;
const viewType = view.type;
const readOnly = !window.sfMetadataContext.canModifyView(view);
const readOnly = window.sfMetadataContext ? !window.sfMetadataContext.canModifyView(view) : true;
return (
<div
className="sf-metadata-tool"
onClick={onHeaderClick}
>
<div className="sf-metadata-tool-left-operations">
{viewType === VIEW_TYPE.GALLERY && (
<>
<GalleryGroupBySetter view={view} />
<GallerySliderSetter view={view} />
</>
)}
<FilterSetter
isNeedSubmit={true}
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-filter"
filtersClassName="sf-metadata-filters"
target="sf-metadata-filter-popover"
{viewType === VIEW_TYPE.TABLE && (
<TableViewToolbar
readOnly={readOnly}
filterConjunction={view.filter_conjunction}
basicFilters={view.basic_filters}
filters={view.filters}
columns={filterColumns}
modifyFilters={modifyFilters}
view={view}
collaborators={collaborators}
viewType={viewType}
/>
<SortSetter
isNeedSubmit={true}
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-sort"
target="sf-metadata-sort-popover"
readOnly={readOnly}
sorts={view.sorts}
type={viewType}
columns={viewColumns}
modifyFilters={modifyFilters}
modifySorts={modifySorts}
/>
{viewType !== VIEW_TYPE.GALLERY && (
<GroupbySetter
isNeedSubmit={true}
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-groupby"
target="sf-metadata-groupby-popover"
readOnly={readOnly}
columns={viewColumns}
groupbys={view.groupbys}
modifyGroupbys={modifyGroupbys}
/>
)}
{viewType !== VIEW_TYPE.GALLERY && (
<HideColumnSetter
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-hide-column"
target="sf-metadata-hide-column-popover"
readOnly={readOnly}
columns={viewColumns.slice(1)}
hiddenColumns={view.hidden_columns || []}
modifyHiddenColumns={modifyHiddenColumns}
modifyColumnOrder={modifyColumnOrder}
/>
)}
</div>
<div className="sf-metadata-tool-right-operations"></div>
{viewType === VIEW_TYPE.GALLERY && (
<GalleryViewToolbar
readOnly={readOnly}
isCustomPermission={isCustomPermission}
view={view}
collaborators={collaborators}
modifyFilters={modifyFilters}
modifySorts={modifySorts}
showDetail={showDetail}
/>
)}
</div>
);
};
ViewToolBar.propTypes = {
viewId: PropTypes.string,
isCustomPermission: PropTypes.bool,
switchViewMode: PropTypes.func,
};
export default ViewToolBar;

View File

@@ -0,0 +1,82 @@
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { FilterSetter, GroupbySetter, SortSetter, HideColumnSetter } from '../../data-process-setter';
import { PRIVATE_COLUMN_KEY } from '../../../constants';
const TableViewToolbar = ({
readOnly, view, collaborators,
modifyFilters, modifySorts, modifyGroupbys, modifyHiddenColumns, modifyColumnOrder
}) => {
const viewType = useMemo(() => view.type, [view]);
const viewColumns = useMemo(() => {
if (!view) return [];
return view.columns;
}, [view]);
const filterColumns = useMemo(() => {
return viewColumns.filter(c => c.key !== PRIVATE_COLUMN_KEY.FILE_TYPE);
}, [viewColumns]);
return (
<>
<div className="sf-metadata-tool-left-operations">
<FilterSetter
isNeedSubmit={true}
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-filter"
filtersClassName="sf-metadata-filters"
target="sf-metadata-filter-popover"
readOnly={readOnly}
filterConjunction={view.filter_conjunction}
basicFilters={view.basic_filters}
filters={view.filters}
columns={filterColumns}
modifyFilters={modifyFilters}
collaborators={collaborators}
viewType={viewType}
/>
<SortSetter
isNeedSubmit={true}
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-sort"
target="sf-metadata-sort-popover"
readOnly={readOnly}
sorts={view.sorts}
type={viewType}
columns={viewColumns}
modifySorts={modifySorts}
/>
<GroupbySetter
isNeedSubmit={true}
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-groupby"
target="sf-metadata-groupby-popover"
readOnly={readOnly}
columns={viewColumns}
groupbys={view.groupbys}
modifyGroupbys={modifyGroupbys}
/>
<HideColumnSetter
wrapperClass="sf-metadata-view-tool-operation-btn sf-metadata-view-tool-hide-column"
target="sf-metadata-hide-column-popover"
readOnly={readOnly}
columns={viewColumns.slice(1)}
hiddenColumns={view.hidden_columns || []}
modifyHiddenColumns={modifyHiddenColumns}
modifyColumnOrder={modifyColumnOrder}
/>
</div>
<div className="sf-metadata-tool-right-operations"></div>
</>
);
};
TableViewToolbar.propTypes = {
readOnly: PropTypes.bool,
view: PropTypes.object.isRequired,
collaborators: PropTypes.array,
modifyFilters: PropTypes.func,
modifySorts: PropTypes.func,
modifyGroupbys: PropTypes.func,
modifyHiddenColumns: PropTypes.func,
modifyColumnOrder: PropTypes.func,
};
export default TableViewToolbar;

View File

@@ -124,6 +124,7 @@ export const MetadataViewProvider = ({
store: storeRef.current,
deleteFilesCallback: params.deleteFilesCallback,
renameFileCallback: params.renameFileCallback,
updateCurrentDirent: params.updateCurrentDirent,
}}
>
{children}

View File

@@ -141,6 +141,7 @@ export const MetadataProvider = ({ repoID, hideMetadataView, selectMetadataView,
parentNode: {},
key: repoID,
view_id: view._id,
view_type: view.type,
};
selectMetadataView(node);
// eslint-disable-next-line react-hooks/exhaustive-deps

View File

@@ -13,7 +13,7 @@ const GalleryMain = ({
gap,
mode,
selectedImages,
setSelectedImages,
onImageSelect,
onImageClick,
onImageDoubleClick,
onImageRightClick
@@ -26,6 +26,7 @@ const GalleryMain = ({
const [selectionStart, setSelectionStart] = useState(null);
const imageHeight = useMemo(() => size + gap, [size, gap]);
const selectedImageIds = useMemo(() => selectedImages.map(img => img.id), [selectedImages]);
const handleMouseDown = useCallback((e) => {
if (e.button !== 0) return;
@@ -33,9 +34,7 @@ const GalleryMain = ({
setIsSelecting(true);
setSelectionStart({ x: e.clientX, y: e.clientY });
setSelectedImages([]);
}, [setSelectedImages]);
}, []);
const handleMouseMove = useCallback((e) => {
if (!isSelecting) return;
@@ -70,9 +69,9 @@ const GalleryMain = ({
});
});
setSelectedImages(selected);
onImageSelect(selected);
});
}, [groups, isSelecting, selectionStart, setSelectedImages]);
}, [groups, isSelecting, selectionStart, onImageSelect]);
const handleMouseUp = useCallback((e) => {
if (e.button !== 0) return;
@@ -113,7 +112,6 @@ const GalleryMain = ({
key={name}
className="metadata-gallery-date-group"
style={{ height, paddingTop }}
>
{mode !== GALLERY_DATE_MODE.ALL && childrenStartIndex === 0 && (
<div className="metadata-gallery-date-tag">{name || gettext('Empty')}</div>
@@ -129,7 +127,7 @@ const GalleryMain = ({
>
{children.slice(childrenStartIndex, childrenEndIndex + 1).map((row) => {
return row.children.map((img) => {
const isSelected = selectedImages.includes(img);
const isSelected = selectedImageIds.includes(img.id);
return (
<div
key={img.src}
@@ -151,7 +149,7 @@ const GalleryMain = ({
</div>
</div>
);
}, [overScan, columns, size, imageHeight, mode, selectedImages, onImageClick, onImageDoubleClick, onImageRightClick]);
}, [overScan, columns, size, imageHeight, mode, selectedImageIds, onImageClick, onImageDoubleClick, onImageRightClick]);
if (!Array.isArray(groups) || groups.length === 0) {
return <EmptyTip text={gettext('No record')}/>;
@@ -195,6 +193,7 @@ GalleryMain.propTypes = {
gap: PropTypes.number.isRequired,
mode: PropTypes.string,
selectedImages: PropTypes.array.isRequired,
onImageSelect: PropTypes.func.isRequired,
onImageClick: PropTypes.func.isRequired,
onImageDoubleClick: PropTypes.func.isRequired,
onImageRightClick: PropTypes.func.isRequired,

View File

@@ -1,5 +1,5 @@
.sf-metadata-gallery-container {
height: calc(100vh - 100px);
height: 100%;
padding: 0 16px;
position: relative;
display: flex;

View File

@@ -13,6 +13,8 @@ import { Utils } from '../../../utils/utils';
import { getDateDisplayString, getFileNameFromRecord, getParentDirFromRecord } from '../../utils/cell';
import { siteRoot, fileServerRoot, useGoFileserver, gettext, thumbnailSizeForGrid, thumbnailSizeForOriginal } from '../../../utils/constants';
import { EVENT_BUS_TYPE, PER_LOAD_NUMBER, PRIVATE_COLUMN_KEY, GALLERY_DATE_MODE, DATE_TAG_HEIGHT, GALLERY_IMAGE_GAP } from '../../constants';
import { getRowById } from '../../utils/table';
import { getEventClassName } from '../../utils/common';
import './index.css';
@@ -31,9 +33,14 @@ const Gallery = () => {
const containerRef = useRef(null);
const renderMoreTimer = useRef(null);
const { metadata, store } = useMetadataView();
const { metadata, store, updateCurrentDirent } = useMetadataView();
const repoID = window.sfMetadataContext.getSetting('repoID');
useEffect(() => {
updateCurrentDirent();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Number of images per row
const columns = useMemo(() => {
return 8 - zoomGear;
@@ -206,21 +213,38 @@ const Gallery = () => {
return groups.flatMap(group => group.children.flatMap(row => row.children));
}, [groups]);
const updateSelectedImage = useCallback((image = null) => {
const imageInfo = image ? getRowById(metadata, image.id) : null;
if (!imageInfo) {
updateCurrentDirent();
return;
}
updateCurrentDirent({
type: 'file',
name: image.name,
path: image.path,
file_tags: []
});
}, [metadata, updateCurrentDirent]);
const handleClick = useCallback((event, image) => {
if (event.metaKey || event.ctrlKey) {
setSelectedImages(prev =>
prev.includes(image) ? prev.filter(img => img !== image) : [...prev, image]
);
updateSelectedImage(image);
} else if (event.shiftKey && selectedImages.length > 0) {
const lastSelected = selectedImages[selectedImages.length - 1];
const start = imageItems.indexOf(lastSelected);
const end = imageItems.indexOf(image);
const range = imageItems.slice(Math.min(start, end), Math.max(start, end) + 1);
setSelectedImages(prev => Array.from(new Set([...prev, ...range])));
updateSelectedImage(null);
} else {
setSelectedImages([image]);
updateSelectedImage(image);
}
}, [imageItems, selectedImages]);
}, [imageItems, selectedImages, updateSelectedImage]);
const handleDoubleClick = useCallback((event, image) => {
const index = imageItems.findIndex(item => item.id === image.id);
@@ -246,6 +270,10 @@ const Gallery = () => {
setImageIndex((prevState) => (prevState + 1) % imageItemsLength);
};
const handleImageSelection = useCallback((selectedImages) => {
setSelectedImages(selectedImages);
}, []);
const closeImagePopup = () => {
setIsImagePopupOpen(false);
};
@@ -308,8 +336,18 @@ const Gallery = () => {
setIsZipDialogOpen(false);
};
const handleClickOutside = useCallback((event) => {
const className = getEventClassName(event);
const isClickInsideImage = className.includes('metadata-gallery-image-item') || className.includes('metadata-gallery-grid-image');
if (!isClickInsideImage && containerRef.current.contains(event.target)) {
handleImageSelection([]);
updateSelectedImage();
}
}, [handleImageSelection, updateSelectedImage]);
return (
<div className="sf-metadata-container">
<div className="sf-metadata-container" onMouseDown={handleClickOutside}>
<div className={`sf-metadata-gallery-container sf-metadata-gallery-container-${mode}`} ref={containerRef} onScroll={handleScroll} >
{!isFirstLoading && (
<>
@@ -321,7 +359,7 @@ const Gallery = () => {
gap={GALLERY_IMAGE_GAP}
mode={mode}
selectedImages={selectedImages}
setSelectedImages={setSelectedImages}
onImageSelect={handleImageSelection}
onImageClick={handleClick}
onImageDoubleClick={handleDoubleClick}
onImageRightClick={handleRightClick}

View File

@@ -22,12 +22,13 @@ import DeleteFolderDialog from '../../components/dialog/delete-folder-dialog';
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
import { PRIVATE_FILE_TYPE } from '../../constants';
import { MetadataProvider, CollaboratorsProvider } from '../../metadata/hooks';
import { LIST_MODE, METADATA_MODE, FACE_RECOGNITION_MODE } from '../../components/dir-view-mode/constants';
import { LIST_MODE, METADATA_MODE, FACE_RECOGNITION_MODE, DIRENT_DETAIL_MODE } from '../../components/dir-view-mode/constants';
import CurDirPath from '../../components/cur-dir-path';
import DirTool from '../../components/cur-dir-path/dir-tool';
import DetailContainer from '../../components/dirent-detail/detail-container';
import DirColumnView from '../../components/dir-view-mode/dir-column-view';
import SelectedDirentsToolbar from '../../components/toolbar/selected-dirents-toolbar';
import { VIEW_TYPE } from '../../metadata/constants';
import '../../css/lib-content-view.css';
@@ -83,7 +84,6 @@ class LibContentView extends React.Component {
dirID: '', // for update dir list
errorMsg: '',
isDirentDetailShow: false,
direntDetailPanelTab: '',
itemsShowLength: 100,
isSessionExpired: false,
isCopyMoveProgressDialogShow: false,
@@ -107,7 +107,11 @@ class LibContentView extends React.Component {
this.unsubscribeEventBus = null;
}
updateCurrentDirent = (deletedDirent) => {
updateCurrentDirent = (dirent = null) => {
this.setState({ currentDirent: dirent });
};
updateCurrentNotExistDirent = (deletedDirent) => {
let { currentDirent } = this.state;
if (currentDirent && deletedDirent.name === currentDirent.name) {
this.setState({ currentDirent: null });
@@ -124,31 +128,16 @@ class LibContentView extends React.Component {
}
};
showDirentDetail = (direntDetailPanelTab) => {
if (direntDetailPanelTab) {
this.setState({ direntDetailPanelTab: direntDetailPanelTab }, () => {
showDirentDetail = () => {
this.setState({ isDirentDetailShow: true });
});
} else {
this.setState({
direntDetailPanelTab: '',
isDirentDetailShow: true
});
}
};
toggleDirentDetail = () => {
this.setState({
direntDetailPanelTab: '',
isDirentDetailShow: !this.state.isDirentDetailShow
});
this.setState({ isDirentDetailShow: !this.state.isDirentDetailShow });
};
closeDirentDetail = () => {
this.setState({
isDirentDetailShow: false,
direntDetailPanelTab: '',
});
this.setState({ isDirentDetailShow: false });
};
componentDidMount() {
@@ -538,14 +527,14 @@ class LibContentView extends React.Component {
window.history.pushState({ url: url, path: filePath }, filePath, url);
};
showFileMetadata = (filePath, viewId) => {
showFileMetadata = (filePath, viewId, viewType) => {
const repoID = this.props.repoID;
const repoInfo = this.state.currentRepoInfo;
this.setState({
currentMode: METADATA_MODE,
path: filePath,
viewId: viewId,
isDirentDetailShow: false
isDirentDetailShow: viewType === VIEW_TYPE.GALLERY ? this.state.isDirentDetailShow : false,
}, () => {
setTimeout(() => {
this.unsubscribeEventBus = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.OPEN_MARKDOWN_DIALOG, this.openMarkDownDialog);
@@ -1020,7 +1009,7 @@ class LibContentView extends React.Component {
if (mode === this.state.currentMode) {
return;
}
if (mode === 'detail') {
if (mode === DIRENT_DETAIL_MODE) {
this.toggleDirentDetail();
return;
}
@@ -1134,7 +1123,7 @@ class LibContentView extends React.Component {
};
onMainPanelItemDelete = (dirent) => {
this.updateCurrentDirent(dirent);
this.updateCurrentNotExistDirent(dirent);
let path = Utils.joinPath(this.state.path, dirent.name);
this.deleteItem(path, dirent.isDir());
};
@@ -1261,7 +1250,7 @@ class LibContentView extends React.Component {
// list operations
onMoveItem = (destRepo, dirent, moveToDirentPath, nodeParentPath) => {
this.updateCurrentDirent(dirent);
this.updateCurrentNotExistDirent(dirent);
let repoID = this.props.repoID;
// just for view list state
let dirName = dirent.name;
@@ -1897,7 +1886,7 @@ class LibContentView extends React.Component {
}
} else if (Utils.isFileMetadata(node?.object?.type)) {
if (node.path !== this.state.path) {
this.showFileMetadata(node.path, node.view_id || '0000');
this.showFileMetadata(node.path, node.view_id || '0000', node.view_type || VIEW_TYPE.TABLE);
}
} else if (Utils.isFaceRecognition(node?.object?.type)) {
if (node.path !== this.state.path) {
@@ -2428,6 +2417,7 @@ class LibContentView extends React.Component {
getMarkDownFilePath={this.getMarkDownFilePath}
getMarkDownFileName={this.getMarkDownFileName}
openMarkdownFile={this.openMarkdownFile}
updateCurrentDirent={this.updateCurrentDirent}
/>
:
<div className="message err-tip">{gettext('Folder does not exist.')}</div>