mirror of
https://github.com/haiwen/seahub.git
synced 2025-10-21 19:00:12 +00:00
card view support toolbar menu and right click menu (#8278)
This commit is contained in:
229
frontend/src/components/toolbar/card-files-toolbar.js
Normal file
229
frontend/src/components/toolbar/card-files-toolbar.js
Normal file
@@ -0,0 +1,229 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ItemDropdownMenu from '../dropdown-menu/item-dropdown-menu';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import { EVENT_BUS_TYPE, PRIVATE_COLUMN_KEY } from '../../metadata/constants';
|
||||
import TextTranslation from '../../utils/text-translation';
|
||||
import RowUtils from '../../metadata/views/table/utils/row-utils';
|
||||
import { checkIsDir } from '../../metadata/utils/row';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { getFileNameFromRecord, getParentDirFromRecord } from '../../metadata/utils/cell';
|
||||
import { openInNewTab, openParentFolder } from '../../metadata/utils/file';
|
||||
import { buildCardToolbarMenuOptions } from '../../metadata/utils/menu-builder';
|
||||
import { useMetadataStatus } from '../../hooks';
|
||||
import { getColumnByKey } from '../sf-table/utils/column';
|
||||
|
||||
const CardFilesToolbar = ({ repoID, updateCurrentDirent }) => {
|
||||
const [selectedRecordIds, setSelectedRecordIds] = useState([]);
|
||||
const [metadata, setMetadata] = useState({});
|
||||
const metadataRef = useRef([]);
|
||||
const menuRef = useRef(null);
|
||||
|
||||
const { enableFaceRecognition, enableTags } = useMetadataStatus();
|
||||
|
||||
const eventBus = window.sfMetadataContext && window.sfMetadataContext.eventBus;
|
||||
|
||||
const records = useMemo(() => selectedRecordIds.map(id => RowUtils.getRecordById(id, metadataRef.current)).filter(Boolean) || [], [selectedRecordIds]);
|
||||
|
||||
const areRecordsInSameFolder = useMemo(() => {
|
||||
if (records.length <= 1) return true;
|
||||
const firstPath = records[0] ? getParentDirFromRecord(records[0]) : null;
|
||||
return firstPath && records.every(record => getParentDirFromRecord(record) === firstPath);
|
||||
}, [records]);
|
||||
|
||||
const readOnly = !window.sfMetadataContext.canModify();
|
||||
const isMultiple = selectedRecordIds.length > 1;
|
||||
|
||||
const toolbarMenuOptions = useMemo(() => {
|
||||
if (!records.length || !metadata.columns) return [];
|
||||
const metadataStatus = {
|
||||
enableFaceRecognition,
|
||||
enableGenerateDescription: getColumnByKey(metadataRef.current.columns, PRIVATE_COLUMN_KEY.FILE_DESCRIPTION) !== null,
|
||||
enableTags
|
||||
};
|
||||
return buildCardToolbarMenuOptions(
|
||||
records,
|
||||
readOnly,
|
||||
metadataStatus,
|
||||
isMultiple,
|
||||
areRecordsInSameFolder,
|
||||
false
|
||||
);
|
||||
}, [records, metadata.columns, enableFaceRecognition, enableTags, readOnly, isMultiple, areRecordsInSameFolder]);
|
||||
|
||||
const unSelect = useCallback(() => {
|
||||
setSelectedRecordIds([]);
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.UPDATE_SELECTED_RECORD_IDS, []);
|
||||
updateCurrentDirent();
|
||||
}, [eventBus, updateCurrentDirent]);
|
||||
|
||||
const deleteRecords = useCallback(() => {
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.DELETE_RECORDS, selectedRecordIds, {
|
||||
success_callback: () => {
|
||||
updateCurrentDirent();
|
||||
}
|
||||
});
|
||||
}, [eventBus, selectedRecordIds, updateCurrentDirent]);
|
||||
|
||||
const toggleMoveDialog = useCallback(() => {
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_MOVE_DIALOG, records);
|
||||
}, [eventBus, records]);
|
||||
|
||||
const toggleCopyDialog = useCallback(() => {
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_COPY_DIALOG, records);
|
||||
}, [eventBus, records]);
|
||||
|
||||
const downloadRecords = useCallback(() => {
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.DOWNLOAD_RECORDS, selectedRecordIds);
|
||||
}, [eventBus, selectedRecordIds]);
|
||||
|
||||
const getMenuList = useCallback(() => {
|
||||
return toolbarMenuOptions;
|
||||
}, [toolbarMenuOptions]);
|
||||
|
||||
const onMenuItemClick = useCallback((operation) => {
|
||||
switch (operation) {
|
||||
case TextTranslation.MOVE.key:
|
||||
case TextTranslation.MOVE_FILE.key:
|
||||
case TextTranslation.MOVE_FOLDER.key: {
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_MOVE_DIALOG, records);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.COPY.key: {
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_COPY_DIALOG, records);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.DOWNLOAD.key: {
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.DOWNLOAD_RECORDS, selectedRecordIds);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.EXTRACT_FILE_DETAIL.key:
|
||||
case TextTranslation.EXTRACT_FILE_DETAILS.key: {
|
||||
const imageOrVideoRecords = records.filter(record => {
|
||||
const isFolder = checkIsDir(record);
|
||||
if (isFolder || readOnly) return false;
|
||||
const fileName = getFileNameFromRecord(record);
|
||||
return Utils.imageCheck(fileName) || Utils.videoCheck(fileName);
|
||||
});
|
||||
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.UPDATE_RECORD_DETAILS, imageOrVideoRecords);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.DETECT_FACES.key: {
|
||||
const imageRecords = records.filter(record => {
|
||||
const isFolder = checkIsDir(record);
|
||||
if (isFolder || readOnly) return false;
|
||||
const fileName = getFileNameFromRecord(record);
|
||||
return Utils.imageCheck(fileName);
|
||||
});
|
||||
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.UPDATE_FACE_RECOGNITION, imageRecords);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.GENERATE_DESCRIPTION.key: {
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.GENERATE_DESCRIPTION, records[0]);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.GENERATE_TAGS.key: {
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.GENERATE_FILE_TAGS, records[0]);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.EXTRACT_TEXT.key: {
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.EXTRACT_TEXT, records[0], menuRef.current.dropdownRef.current);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.OPEN_FILE_IN_NEW_TAB.key:
|
||||
case TextTranslation.OPEN_FOLDER_IN_NEW_TAB.key: {
|
||||
openInNewTab(repoID, records[0]);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.OPEN_PARENT_FOLDER.key: {
|
||||
openParentFolder(records[0]);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.RENAME.key: {
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_CARD_RENAME_DIALOG);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, [eventBus, records, selectedRecordIds, readOnly, repoID]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeSelectedFileIds = eventBus && eventBus.subscribe(EVENT_BUS_TYPE.SELECT_RECORDS, (ids, metadataObj) => {
|
||||
metadataRef.current = metadataObj || [];
|
||||
setMetadata(metadataObj || {});
|
||||
setSelectedRecordIds(ids);
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribeSelectedFileIds && unsubscribeSelectedFileIds();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const length = selectedRecordIds.length;
|
||||
|
||||
return (
|
||||
<div className="selected-dirents-toolbar">
|
||||
<span className="cur-view-path-btn px-2" onClick={unSelect}>
|
||||
<span className="sf3-font-x-01 sf3-font mr-2" aria-label={gettext('Unselect')} title={gettext('Unselect')}></span>
|
||||
<span>{length}{' '}{gettext('selected')}</span>
|
||||
</span>
|
||||
|
||||
{!isMultiple && !readOnly && (
|
||||
<>
|
||||
<span
|
||||
className="cur-view-path-btn"
|
||||
onClick={toggleMoveDialog}
|
||||
title={gettext('Move')}
|
||||
>
|
||||
<span className="sf3-font-move1 sf3-font" aria-label={gettext('Move')}></span>
|
||||
</span>
|
||||
<span
|
||||
className="cur-view-path-btn"
|
||||
onClick={toggleCopyDialog}
|
||||
title={gettext('Copy')}
|
||||
>
|
||||
<span className="sf3-font-copy1 sf3-font" aria-label={gettext('Copy')}></span>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
<span
|
||||
className="cur-view-path-btn"
|
||||
onClick={downloadRecords}
|
||||
title={gettext('Download')}
|
||||
>
|
||||
<span className="sf3-font-download1 sf3-font" aria-label={gettext('Download')}></span>
|
||||
</span>
|
||||
|
||||
{!readOnly && (
|
||||
<span
|
||||
className="cur-view-path-btn"
|
||||
onClick={deleteRecords}
|
||||
title={gettext('Delete')}
|
||||
>
|
||||
<span className="sf3-font-delete1 sf3-font" aria-label={gettext('Delete')}></span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{length > 0 && (
|
||||
<ItemDropdownMenu
|
||||
ref={menuRef}
|
||||
item={{}}
|
||||
toggleClass="cur-view-path-btn sf3-font-more sf3-font"
|
||||
onMenuItemClick={onMenuItemClick}
|
||||
getMenuList={getMenuList}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CardFilesToolbar.propTypes = {
|
||||
repoID: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default CardFilesToolbar;
|
@@ -10,6 +10,7 @@ import TableFilesToolbar from './table-files-toolbar';
|
||||
import GalleryFilesToolbar from './gallery-files-toolbar';
|
||||
import FaceRecognitionFilesToolbar from './face-recognition-files-toolbar';
|
||||
import KanbanFilesToolbar from './kanban-files-toolbar';
|
||||
import CardFilesToolbar from './card-files-toolbar';
|
||||
|
||||
const ViewToolbar = ({ repoID, repoInfo, mode, path, viewId, updateCurrentDirent }) => {
|
||||
const { idViewMap } = useMetadata();
|
||||
@@ -36,6 +37,10 @@ const ViewToolbar = ({ repoID, repoInfo, mode, path, viewId, updateCurrentDirent
|
||||
return <KanbanFilesToolbar repoID={repoID} updateCurrentDirent={updateCurrentDirent} />;
|
||||
}
|
||||
|
||||
if (type === VIEW_TYPE.CARD) {
|
||||
return <CardFilesToolbar repoID={repoID} updateCurrentDirent={updateCurrentDirent} />;
|
||||
}
|
||||
|
||||
if (mode === TAGS_MODE) {
|
||||
const isAllTagsView = path.split('/').pop() === ALL_TAGS_ID;
|
||||
if (isAllTagsView) {
|
||||
|
@@ -50,13 +50,13 @@ const SortSetter = ({ target = 'sf-metadata-sort-popover', type, sorts: propsSor
|
||||
}, [modifySorts]);
|
||||
|
||||
if (!columns) return null;
|
||||
const className = classnames(wrapperClass, { 'active': sorts.length > 0 });
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconBtn
|
||||
symbol="sort"
|
||||
size={24}
|
||||
className={className}
|
||||
className={classnames(wrapperClass, { 'active': sorts.length > 0 })}
|
||||
onClick={onSetterToggle}
|
||||
role="button"
|
||||
onKeyDown={onKeyDown}
|
||||
@@ -78,7 +78,6 @@ const SortSetter = ({ target = 'sf-metadata-sort-popover', type, sorts: propsSor
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@@ -96,6 +96,8 @@ export const EVENT_BUS_TYPE = {
|
||||
TOGGLE_CARD_SETTINGS: 'toggle_card_settings',
|
||||
OPEN_CARD_SETTINGS: 'open_card_settings',
|
||||
CLOSE_CARD_SETTINGS: 'close_card_settings',
|
||||
DISPLAY_SORTS: 'display_sorts',
|
||||
TOGGLE_CARD_RENAME_DIALOG: 'toggle_card_rename_dialog',
|
||||
|
||||
// kanban
|
||||
TOGGLE_KANBAN_SETTINGS: 'toggle_kanban_settings',
|
||||
|
@@ -572,3 +572,102 @@ export const buildKanbanToolbarMenuOptions = (records, readOnly, metadataStatus)
|
||||
}
|
||||
return menuOptions;
|
||||
};
|
||||
|
||||
export const buildCardToolbarMenuOptions = (records, readOnly, metadataStatus) => {
|
||||
if (!records || records.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const menuOptions = [
|
||||
{
|
||||
key: TextTranslation.OPEN_FILE_IN_NEW_TAB.key,
|
||||
value: TextTranslation.OPEN_FILE_IN_NEW_TAB.value
|
||||
},
|
||||
{
|
||||
key: TextTranslation.OPEN_PARENT_FOLDER.key,
|
||||
value: TextTranslation.OPEN_PARENT_FOLDER.value
|
||||
}
|
||||
];
|
||||
if (!readOnly) {
|
||||
menuOptions.push('Divider');
|
||||
menuOptions.push(
|
||||
{
|
||||
key: TextTranslation.RENAME.key,
|
||||
value: TextTranslation.RENAME.value
|
||||
}
|
||||
);
|
||||
}
|
||||
const aiOptions = buildAISubmenuOptions(records, readOnly, metadataStatus);
|
||||
if (aiOptions.length > 0) {
|
||||
if (menuOptions.length > 0) {
|
||||
menuOptions.push('Divider');
|
||||
}
|
||||
menuOptions.push(
|
||||
{
|
||||
key: 'AI',
|
||||
value: gettext('AI'),
|
||||
subOpList: aiOptions
|
||||
}
|
||||
);
|
||||
}
|
||||
return menuOptions;
|
||||
};
|
||||
|
||||
export const buildCardMenuOptions = (records, readOnly, metadataStatus) => {
|
||||
if (!records || records.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const menuOptions = [];
|
||||
|
||||
menuOptions.push({
|
||||
key: TextTranslation.OPEN_FILE_IN_NEW_TAB.key,
|
||||
value: TextTranslation.OPEN_FILE_IN_NEW_TAB.value
|
||||
});
|
||||
|
||||
menuOptions.push({
|
||||
key: TextTranslation.OPEN_PARENT_FOLDER.key,
|
||||
value: TextTranslation.OPEN_PARENT_FOLDER.value
|
||||
});
|
||||
|
||||
menuOptions.push('Divider');
|
||||
|
||||
if (!readOnly) {
|
||||
menuOptions.push({
|
||||
key: TextTranslation.RENAME.key,
|
||||
value: TextTranslation.RENAME.value
|
||||
});
|
||||
|
||||
menuOptions.push({
|
||||
key: TextTranslation.MOVE.key,
|
||||
value: TextTranslation.MOVE.value
|
||||
});
|
||||
|
||||
menuOptions.push({
|
||||
key: TextTranslation.COPY.key,
|
||||
value: TextTranslation.COPY.value
|
||||
});
|
||||
}
|
||||
|
||||
menuOptions.push({
|
||||
key: TextTranslation.DOWNLOAD.key,
|
||||
value: TextTranslation.DOWNLOAD.value
|
||||
});
|
||||
|
||||
if (!readOnly) {
|
||||
menuOptions.push({
|
||||
key: TextTranslation.DELETE.key,
|
||||
value: TextTranslation.DELETE.value
|
||||
});
|
||||
}
|
||||
|
||||
const aiOptions = buildAISubmenuOptions(records, readOnly, metadataStatus);
|
||||
if (aiOptions.length > 0) {
|
||||
menuOptions.push('Divider');
|
||||
menuOptions.push({
|
||||
key: 'AI',
|
||||
value: gettext('AI'),
|
||||
subOpList: aiOptions
|
||||
});
|
||||
}
|
||||
return menuOptions;
|
||||
};
|
||||
|
@@ -2,7 +2,7 @@ import React, { useMemo, useCallback, useState, useRef, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { useMetadataView } from '../../../hooks/metadata-view';
|
||||
import { CARD_SETTINGS_KEYS, PRIVATE_COLUMN_KEY } from '../../../constants';
|
||||
import { CARD_SETTINGS_KEYS, PRIVATE_COLUMN_KEY, EVENT_BUS_TYPE } from '../../../constants';
|
||||
import { gettext } from '../../../../utils/constants';
|
||||
import { getRecordIdFromRecord, getFileNameFromRecord, getParentDirFromRecord } from '../../../utils/cell';
|
||||
import { openFile } from '../../../utils/file';
|
||||
@@ -23,7 +23,19 @@ const CardItems = ({ modifyRecord, deleteRecords, modifyColumnData, onCloseSetti
|
||||
const currentImageRef = useRef(null);
|
||||
const containerRef = useRef(null);
|
||||
|
||||
const { isDirentDetailShow, metadata, updateCurrentDirent, showDirentDetail } = useMetadataView();
|
||||
const eventBus = window.sfMetadataContext && window.sfMetadataContext.eventBus;
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = eventBus && eventBus.subscribe(EVENT_BUS_TYPE.UPDATE_SELECTED_RECORD_IDS, (ids) => {
|
||||
setSelectedCard(Array.isArray(ids) ? ids[0] : null);
|
||||
});
|
||||
return () => {
|
||||
unsubscribe && unsubscribe();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const { isDirentDetailShow, metadata, updateCurrentDirent, showDirentDetail, updateSelectedRecordIds } = useMetadataView();
|
||||
const { tagsData } = useTags();
|
||||
|
||||
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||
@@ -88,7 +100,8 @@ const CardItems = ({ modifyRecord, deleteRecords, modifyColumnData, onCloseSetti
|
||||
file_tags: []
|
||||
});
|
||||
setSelectedCard(recordId);
|
||||
}, [updateCurrentDirent]);
|
||||
updateSelectedRecordIds([recordId]);
|
||||
}, [updateCurrentDirent, updateSelectedRecordIds]);
|
||||
|
||||
const onSelectCard = useCallback((record) => {
|
||||
const recordId = getRecordIdFromRecord(record);
|
||||
@@ -100,8 +113,9 @@ const CardItems = ({ modifyRecord, deleteRecords, modifyColumnData, onCloseSetti
|
||||
|
||||
const handleClickOutside = useCallback((event) => {
|
||||
setSelectedCard(null);
|
||||
updateSelectedRecordIds([]);
|
||||
updateCurrentDirent();
|
||||
}, [updateCurrentDirent]);
|
||||
}, [updateCurrentDirent, updateSelectedRecordIds]);
|
||||
|
||||
const onContextMenu = useCallback((event, recordId) => {
|
||||
event.preventDefault();
|
||||
@@ -114,10 +128,11 @@ const CardItems = ({ modifyRecord, deleteRecords, modifyColumnData, onCloseSetti
|
||||
deleteRecords(recordIds, {
|
||||
success_callback: () => {
|
||||
setSelectedCard(null);
|
||||
updateSelectedRecordIds([]);
|
||||
updateCurrentDirent();
|
||||
},
|
||||
});
|
||||
}, [deleteRecords, updateCurrentDirent]);
|
||||
}, [deleteRecords, updateCurrentDirent, updateSelectedRecordIds]);
|
||||
|
||||
const onRename = useCallback((rowId, updates, oldRowData, originalUpdates, originalOldRowData, { success_callback }) => {
|
||||
modifyRecord(rowId, updates, oldRowData, originalUpdates, originalOldRowData, {
|
||||
@@ -132,8 +147,9 @@ const CardItems = ({ modifyRecord, deleteRecords, modifyColumnData, onCloseSetti
|
||||
useEffect(() => {
|
||||
if (!isDirentDetailShow) {
|
||||
setSelectedCard(null);
|
||||
updateSelectedRecordIds([]);
|
||||
}
|
||||
}, [isDirentDetailShow]);
|
||||
}, [isDirentDetailShow, updateSelectedRecordIds]);
|
||||
|
||||
if (records.length == 0) {
|
||||
return <EmptyTip text={gettext('No items')} />;
|
||||
|
@@ -1,54 +1,52 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ContextMenu from '../../../components/context-menu';
|
||||
import RenameDialog from '../../../components/dialog/rename-dialog';
|
||||
import { getRowById } from '../../../../components/sf-table/utils/table';
|
||||
import { checkIsDir } from '../../../utils/row';
|
||||
import { getFileNameFromRecord, getParentDirFromRecord } from '../../../utils/cell';
|
||||
import { gettext } from '../../../../utils/constants';
|
||||
import { openInNewTab, openParentFolder } from '../../../utils/file';
|
||||
import { useMetadataView } from '../../../hooks/metadata-view';
|
||||
import { PRIVATE_COLUMN_KEY } from '../../../constants';
|
||||
import { PRIVATE_COLUMN_KEY, EVENT_BUS_TYPE } from '../../../constants';
|
||||
import { useFileOperations } from '../../../../hooks/file-operations';
|
||||
import { buildCardMenuOptions } from '../../../utils/menu-builder';
|
||||
import { useMetadataStatus } from '../../../../hooks/metadata-status';
|
||||
import { getColumnByKey } from '../../../utils/column';
|
||||
import TextTranslation from '../../../../utils/text-translation';
|
||||
|
||||
const CONTEXT_MENU_KEY = {
|
||||
OPEN_IN_NEW_TAB: 'open_in_new_tab',
|
||||
OPEN_PARENT_FOLDER: 'open_parent_folder',
|
||||
DOWNLOAD: 'download',
|
||||
DELETE: 'delete',
|
||||
RENAME: 'rename',
|
||||
};
|
||||
|
||||
const KanbanContextMenu = ({ selectedCard, onDelete, onRename }) => {
|
||||
const CardContextMenu = ({ selectedCard, onDelete, onRename }) => {
|
||||
const [isRenameDialogShow, setIsRenameDialogShow] = useState(false);
|
||||
|
||||
const { metadata } = useMetadataView();
|
||||
const { enableFaceRecognition, enableTags } = useMetadataStatus();
|
||||
const { handleDownload: handleDownloadAPI } = useFileOperations();
|
||||
const {
|
||||
metadata,
|
||||
updateRecordDetails,
|
||||
updateFaceRecognition,
|
||||
updateRecordDescription,
|
||||
onOCR,
|
||||
generateFileTags
|
||||
} = useMetadataView();
|
||||
|
||||
const selectedRecord = useMemo(() => getRowById(metadata, selectedCard), [metadata, selectedCard]);
|
||||
const isDir = useMemo(() => checkIsDir(selectedRecord), [selectedRecord]);
|
||||
const oldName = useMemo(() => getFileNameFromRecord(selectedRecord), [selectedRecord]);
|
||||
const parentDir = useMemo(() => getParentDirFromRecord(selectedRecord), [selectedRecord]);
|
||||
|
||||
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||
const checkCanDeleteRow = window.sfMetadataContext.checkCanDeleteRow();
|
||||
const canModifyRow = window.sfMetadataContext.canModifyRow();
|
||||
const readOnly = !window.sfMetadataContext.canModify();
|
||||
const record = useMemo(() => getRowById(metadata, selectedCard), [metadata, selectedCard]);
|
||||
|
||||
const options = useMemo(() => {
|
||||
let validOptions = [
|
||||
{ value: CONTEXT_MENU_KEY.OPEN_IN_NEW_TAB, label: isDir ? gettext('Open folder in new tab') : gettext('Open file in new tab') },
|
||||
{ value: CONTEXT_MENU_KEY.OPEN_PARENT_FOLDER, label: gettext('Open parent folder') },
|
||||
{ value: CONTEXT_MENU_KEY.DOWNLOAD, label: gettext('Download') },
|
||||
];
|
||||
if (checkCanDeleteRow) {
|
||||
validOptions.push({ value: CONTEXT_MENU_KEY.DELETE, label: isDir ? gettext('Delete folder') : gettext('Delete file') });
|
||||
}
|
||||
if (canModifyRow) {
|
||||
validOptions.push({ value: CONTEXT_MENU_KEY.RENAME, label: isDir ? gettext('Rename folder') : gettext('Rename file') });
|
||||
}
|
||||
|
||||
return validOptions;
|
||||
}, [isDir, checkCanDeleteRow, canModifyRow]);
|
||||
const metadataStatus = {
|
||||
enableFaceRecognition,
|
||||
enableGenerateDescription: getColumnByKey(metadata.columns, PRIVATE_COLUMN_KEY.FILE_DESCRIPTION) !== null,
|
||||
enableTags
|
||||
};
|
||||
return buildCardMenuOptions(
|
||||
[record],
|
||||
readOnly,
|
||||
metadataStatus,
|
||||
);
|
||||
}, [enableFaceRecognition, metadata.columns, enableTags, record, readOnly]);
|
||||
|
||||
const openRenameDialog = useCallback(() => {
|
||||
setIsRenameDialogShow(true);
|
||||
@@ -72,43 +70,72 @@ const KanbanContextMenu = ({ selectedCard, onDelete, onRename }) => {
|
||||
}, [handleDownloadAPI, parentDir, oldName, isDir]);
|
||||
|
||||
const handleOptionClick = useCallback((option) => {
|
||||
if (!selectedCard) return;
|
||||
const record = getRowById(metadata, selectedCard);
|
||||
if (!record) return;
|
||||
|
||||
switch (option.value) {
|
||||
case CONTEXT_MENU_KEY.OPEN_IN_NEW_TAB: {
|
||||
switch (option.key) {
|
||||
case TextTranslation.OPEN_FILE_IN_NEW_TAB.key: {
|
||||
openInNewTab(repoID, record);
|
||||
break;
|
||||
}
|
||||
case CONTEXT_MENU_KEY.OPEN_PARENT_FOLDER: {
|
||||
case TextTranslation.OPEN_PARENT_FOLDER.key: {
|
||||
openParentFolder(record);
|
||||
break;
|
||||
}
|
||||
case CONTEXT_MENU_KEY.DOWNLOAD: {
|
||||
handleDownload(record);
|
||||
case TextTranslation.RENAME.key: {
|
||||
openRenameDialog();
|
||||
break;
|
||||
}
|
||||
case CONTEXT_MENU_KEY.DELETE: {
|
||||
case TextTranslation.MOVE.key:
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_MOVE_DIALOG, [record]);
|
||||
break;
|
||||
case TextTranslation.COPY.key:
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_COPY_DIALOG, [record]);
|
||||
break;
|
||||
case TextTranslation.DOWNLOAD.key: {
|
||||
handleDownload();
|
||||
break;
|
||||
}
|
||||
case TextTranslation.DELETE.key: {
|
||||
onDelete([selectedCard]);
|
||||
break;
|
||||
}
|
||||
case CONTEXT_MENU_KEY.RENAME: {
|
||||
openRenameDialog();
|
||||
case TextTranslation.DETECT_FACES.key: {
|
||||
updateFaceRecognition([record]);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.EXTRACT_FILE_DETAIL.key: {
|
||||
updateRecordDetails([record]);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.GENERATE_DESCRIPTION.key: {
|
||||
updateRecordDescription(record);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.GENERATE_TAGS.key: {
|
||||
generateFileTags(record);
|
||||
break;
|
||||
}
|
||||
case TextTranslation.EXTRACT_TEXT.key: {
|
||||
onOCR(record, '.sf-metadata-card-item-image-container');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, [metadata, repoID, selectedCard, onDelete, openRenameDialog, handleDownload]);
|
||||
}, [record, updateFaceRecognition, repoID, openRenameDialog, handleDownload, onDelete, selectedCard, updateRecordDetails, updateRecordDescription, generateFileTags, onOCR]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.TOGGLE_CARD_RENAME_DIALOG, openRenameDialog);
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [openRenameDialog]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ContextMenu
|
||||
options={options}
|
||||
onOptionClick={handleOptionClick}
|
||||
allowedTriggerElements={['.sf-metadata-kanban-card']}
|
||||
allowedTriggerElements={['.sf-metadata-view-card']}
|
||||
/>
|
||||
{isRenameDialogShow && (
|
||||
<RenameDialog
|
||||
@@ -122,10 +149,10 @@ const KanbanContextMenu = ({ selectedCard, onDelete, onRename }) => {
|
||||
);
|
||||
};
|
||||
|
||||
KanbanContextMenu.propTypes = {
|
||||
CardContextMenu.propTypes = {
|
||||
selectedCard: PropTypes.string,
|
||||
onDelete: PropTypes.func,
|
||||
onRename: PropTypes.func,
|
||||
};
|
||||
|
||||
export default KanbanContextMenu;
|
||||
export default CardContextMenu;
|
||||
|
Reference in New Issue
Block a user