mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-09 10:50:24 +00:00
feat: optimize code
This commit is contained in:
@@ -120,7 +120,12 @@ class DirTool extends React.Component {
|
||||
if (isFileExtended) {
|
||||
return (
|
||||
<div className="dir-tool">
|
||||
<MetadataViewToolBar viewId={viewId} isCustomPermission={isCustomPermission} showDetail={this.showDirentDetail} closeDetail={this.props.onCloseDetail} />
|
||||
<MetadataViewToolBar
|
||||
viewId={viewId}
|
||||
isCustomPermission={isCustomPermission}
|
||||
showDetail={this.showDirentDetail}
|
||||
closeDetail={this.props.onCloseDetail}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import { GRID_MODE, LIST_MODE, METADATA_MODE } from './constants';
|
||||
const propTypes = {
|
||||
isSidePanelFolded: PropTypes.bool,
|
||||
isTreePanelShown: PropTypes.bool.isRequired,
|
||||
isDirentDetailShow: PropTypes.bool,
|
||||
currentMode: PropTypes.string.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
repoID: PropTypes.string.isRequired,
|
||||
@@ -198,6 +199,7 @@ class DirColumnView extends React.Component {
|
||||
{currentMode === METADATA_MODE && (
|
||||
<SeafileMetadata
|
||||
mediaUrl={mediaUrl}
|
||||
isDirentDetailShow={this.props.isDirentDetailShow}
|
||||
repoID={this.props.repoID}
|
||||
repoInfo={this.props.currentRepoInfo}
|
||||
viewID={this.props.viewId}
|
||||
|
@@ -17,6 +17,7 @@ const ViewDetails = ({ viewId, onClose }) => {
|
||||
if (type === VIEW_TYPE.GALLERY) return `${mediaUrl}favicons/gallery.png`;
|
||||
if (type === VIEW_TYPE.TABLE) return `${mediaUrl}favicons/table.png`;
|
||||
if (type === VIEW_TYPE.FACE_RECOGNITION) return `${mediaUrl}favicons/face-recognition-view.png`;
|
||||
if (type === VIEW_TYPE.KANBAN) return `${mediaUrl}favicons/kanban.png`;
|
||||
return `${mediaUrl}img/file/256/file.png`;
|
||||
}, [view]);
|
||||
|
||||
|
@@ -100,11 +100,13 @@ const ViewToolBar = ({ viewId, isCustomPermission, showDetail, closeDetail }) =>
|
||||
)}
|
||||
{viewType === VIEW_TYPE.KANBAN && (
|
||||
<KanbanViewToolBar
|
||||
isCustomPermission={isCustomPermission}
|
||||
readOnly={readOnly}
|
||||
view={view}
|
||||
collaborators={collaborators}
|
||||
modifyFilters={modifyFilters}
|
||||
modifySorts={modifySorts}
|
||||
showDetail={showDetail}
|
||||
closeDetail={closeDetail}
|
||||
/>
|
||||
)}
|
||||
|
@@ -1,15 +1,18 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { IconBtn } from '@seafile/sf-metadata-ui-component';
|
||||
import { EVENT_BUS_TYPE, PRIVATE_COLUMN_KEY } from '../../../constants';
|
||||
import { FilterSetter, SortSetter } from '../../data-process-setter';
|
||||
import { gettext } from '../../../../utils/constants';
|
||||
|
||||
const KanbanViewToolBar = ({
|
||||
isCustomPermission,
|
||||
readOnly,
|
||||
view,
|
||||
collaborators,
|
||||
modifyFilters,
|
||||
modifySorts,
|
||||
showDetail,
|
||||
closeDetail,
|
||||
}) => {
|
||||
const viewType = useMemo(() => view.type, [view]);
|
||||
@@ -27,6 +30,11 @@ const KanbanViewToolBar = ({
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_KANBAN_SETTINGS);
|
||||
};
|
||||
|
||||
const toggleDetails = useCallback(() => {
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.CLOSE_KANBAN_SETTINGS);
|
||||
showDetail();
|
||||
}, [showDetail]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="sf-metadata-tool-left-operations">
|
||||
@@ -63,6 +71,11 @@ const KanbanViewToolBar = ({
|
||||
tabIndex={0}
|
||||
onClick={onToggleKanbanSetting}
|
||||
/>
|
||||
{!isCustomPermission && (
|
||||
<div className="cur-view-path-btn ml-2" onClick={toggleDetails}>
|
||||
<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>
|
||||
</>
|
||||
|
@@ -60,4 +60,6 @@ export const EVENT_BUS_TYPE = {
|
||||
|
||||
// kanban
|
||||
TOGGLE_KANBAN_SETTINGS: 'toggle_kanban_settings',
|
||||
OPEN_KANBAN_SETTINGS: 'open_kanban_settings',
|
||||
CLOSE_KANBAN_SETTINGS: 'close_kanban_settings',
|
||||
};
|
||||
|
@@ -135,9 +135,3 @@ export const GALLERY_DATE_MODE = {
|
||||
};
|
||||
|
||||
export const UNCATEGORIZED = '_uncategorized';
|
||||
|
||||
export const FILE_TYPE = {
|
||||
MARKDOWN: 'markdown',
|
||||
SDOC: 'sdoc',
|
||||
IMAGE: 'image',
|
||||
};
|
||||
|
@@ -130,6 +130,7 @@ export const MetadataViewProvider = ({
|
||||
isLoading,
|
||||
metadata,
|
||||
store: storeRef.current,
|
||||
isDirentDetailShow: params.isDirentDetailShow,
|
||||
deleteFilesCallback: params.deleteFilesCallback,
|
||||
renameFileCallback: params.renameFileCallback,
|
||||
updateCurrentDirent: params.updateCurrentDirent,
|
||||
|
97
frontend/src/metadata/utils/open-file.js
Normal file
97
frontend/src/metadata/utils/open-file.js
Normal file
@@ -0,0 +1,97 @@
|
||||
import { getFileNameFromRecord, getParentDirFromRecord } from './cell';
|
||||
import { checkIsDir } from './row';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { siteRoot } from '../../utils/constants';
|
||||
import { EVENT_BUS_TYPE } from '../../components/common/event-bus-type';
|
||||
|
||||
const FILE_TYPE = {
|
||||
FOLDER: 'folder',
|
||||
MARKDOWN: 'markdown',
|
||||
SDOC: 'sdoc',
|
||||
IMAGE: 'image',
|
||||
};
|
||||
|
||||
const _getFileType = (fileName, isDir) => {
|
||||
if (isDir) return FILE_TYPE.FOLDER;
|
||||
if (!fileName) return '';
|
||||
const index = fileName.lastIndexOf('.');
|
||||
if (index === -1) return '';
|
||||
const suffix = fileName.slice(index).toLowerCase();
|
||||
if (suffix.indexOf(' ') > -1) return '';
|
||||
if (Utils.imageCheck(fileName)) return FILE_TYPE.IMAGE;
|
||||
if (Utils.isMarkdownFile(fileName)) return FILE_TYPE.MARKDOWN;
|
||||
if (Utils.isSdocFile(fileName)) return FILE_TYPE.SDOC;
|
||||
return '';
|
||||
};
|
||||
|
||||
const _getParentDir = (record) => {
|
||||
const parentDir = getParentDirFromRecord(record);
|
||||
if (parentDir === '/') {
|
||||
return '';
|
||||
}
|
||||
return parentDir;
|
||||
};
|
||||
|
||||
const _generateUrl = (fileName, parentDir) => {
|
||||
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||
const path = Utils.encodePath(Utils.joinPath(parentDir, fileName));
|
||||
return `${siteRoot}lib/${repoID}/file${path}`;
|
||||
};
|
||||
|
||||
const _openUrl = (url) => {
|
||||
window.open(url);
|
||||
};
|
||||
|
||||
const _openMarkdown = (fileName, parentDir, eventBus) => {
|
||||
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.OPEN_MARKDOWN_DIALOG, parentDir, fileName);
|
||||
};
|
||||
|
||||
const _openByNewWindow = (fileName, parentDir, fileType) => {
|
||||
if (!fileType) {
|
||||
const url = _generateUrl(fileName, parentDir);
|
||||
_openUrl(url);
|
||||
return;
|
||||
}
|
||||
let pathname = window.location.pathname;
|
||||
if (pathname.endsWith('/')) {
|
||||
pathname = pathname.slice(0, -1);
|
||||
}
|
||||
_openUrl(window.location.origin + pathname + Utils.encodePath(Utils.joinPath(parentDir, fileName)));
|
||||
};
|
||||
|
||||
const _openSdoc = (fileName, parentDir) => {
|
||||
const url = _generateUrl(fileName, parentDir);
|
||||
_openUrl(url);
|
||||
};
|
||||
|
||||
const _openOthers = (fileName, parentDir, fileType) => {
|
||||
_openByNewWindow(fileName, parentDir, fileType);
|
||||
};
|
||||
|
||||
export const openFile = (record, eventBus, _openImage = () => {}) => {
|
||||
if (!record) return;
|
||||
const fileName = getFileNameFromRecord(record);
|
||||
const isDir = checkIsDir(record);
|
||||
const parentDir = _getParentDir(record);
|
||||
const fileType = _getFileType(fileName, isDir);
|
||||
|
||||
switch (fileType) {
|
||||
case FILE_TYPE.MARKDOWN: {
|
||||
_openMarkdown(fileName, parentDir, eventBus);
|
||||
break;
|
||||
}
|
||||
case FILE_TYPE.SDOC: {
|
||||
_openSdoc(fileName, parentDir);
|
||||
break;
|
||||
}
|
||||
case FILE_TYPE.IMAGE: {
|
||||
_openImage(record);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
_openOthers(fileName, parentDir, fileType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -12,21 +12,32 @@
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.sf-metadata-kanban-card.selected {
|
||||
border-color: #FF8000;
|
||||
}
|
||||
|
||||
.sf-metadata-kanban-card:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0 0 6px #0000002e;
|
||||
}
|
||||
|
||||
.sf-metadata-kanban-card.readonly {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.sf-metadata-kanban-card .sf-metadata-kanban-card-header {
|
||||
font-weight: 500;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sf-metadata-kanban-card .sf-metadata-kanban-card-header .sf-metadata-file-name {
|
||||
text-decoration: none;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.sf-metadata-kanban-card .sf-metadata-kanban-card-header .sf-metadata-file-name:hover {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: #212529;
|
||||
}
|
||||
|
||||
.sf-metadata-kanban-card .sf-metadata-kanban-card-body .sf-metadata-kanban-card-record:first-child {
|
||||
margin-top: 10px;
|
||||
}
|
||||
@@ -84,17 +95,6 @@
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.sf-metadata-kanban-card .sf-metadata-ui.file-name-formatter .sf-metadata-file-name {
|
||||
text-decoration: none;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.sf-metadata-kanban-card .sf-metadata-ui.file-name-formatter .sf-metadata-file-name:hover {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: #212529;
|
||||
}
|
||||
|
||||
.sf-metadata-kanban-card .sf-metadata-kanban-card-record .collaborators-formatter {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
@@ -1,98 +1,49 @@
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import Formatter from '../formatter';
|
||||
import { FILE_TYPE } from '../../../../../constants';
|
||||
import { useMetadataView } from '../../../../../hooks/metadata-view';
|
||||
import { Utils } from '../../../../../../utils/utils';
|
||||
import { geRecordIdFromRecord, getCellValueByColumn, getFileNameFromRecord, getParentDirFromRecord, isValidCellValue } from '../../../../../utils/cell';
|
||||
import { getCellValueByColumn, isValidCellValue } from '../../../../../utils/cell';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const Card = ({
|
||||
readonly,
|
||||
isSelected,
|
||||
displayEmptyValue,
|
||||
displayColumnName,
|
||||
record,
|
||||
titleColumn,
|
||||
displayColumns,
|
||||
onCloseSettings,
|
||||
onOpenFile,
|
||||
onSelectCard,
|
||||
}) => {
|
||||
const cardRef = useRef(null);
|
||||
|
||||
const { updateCurrentDirent, showDirentDetail } = useMetadataView();
|
||||
|
||||
const titleValue = getCellValueByColumn(record, titleColumn);
|
||||
|
||||
const handleClickCard = useCallback((e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const name = getFileNameFromRecord(record);
|
||||
const path = getParentDirFromRecord(record);
|
||||
updateCurrentDirent({
|
||||
type: 'file',
|
||||
name,
|
||||
path,
|
||||
file_tags: []
|
||||
});
|
||||
onCloseSettings();
|
||||
showDirentDetail();
|
||||
}, [record, updateCurrentDirent, showDirentDetail, onCloseSettings]);
|
||||
const handleClickCard = useCallback((event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.nativeEvent.stopImmediatePropagation();
|
||||
onSelectCard(record);
|
||||
}, [record, onSelectCard]);
|
||||
|
||||
const getFileType = useCallback((fileName) => {
|
||||
if (!fileName) return '';
|
||||
const index = fileName.lastIndexOf('.');
|
||||
if (index === -1) return '';
|
||||
const suffix = fileName.slice(index).toLowerCase();
|
||||
if (suffix.indexOf(' ') > -1) return '';
|
||||
if (Utils.imageCheck(fileName)) return FILE_TYPE.IMAGE;
|
||||
if (Utils.isMarkdownFile(fileName)) return FILE_TYPE.MARKDOWN;
|
||||
if (Utils.isSdocFile(fileName)) return FILE_TYPE.SDOC;
|
||||
return '';
|
||||
}, []);
|
||||
|
||||
const handleClickFilename = useCallback((e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const fileName = getFileNameFromRecord(record);
|
||||
const fileType = getFileType(fileName);
|
||||
const parentDir = getParentDirFromRecord(record);
|
||||
const recordId = geRecordIdFromRecord(record);
|
||||
onOpenFile(fileType, fileName, parentDir, recordId);
|
||||
}, [record, getFileType, onOpenFile]);
|
||||
|
||||
useEffect(() => {
|
||||
const cardElement = cardRef.current;
|
||||
if (!cardElement) return;
|
||||
|
||||
const filenameElement = cardElement.querySelector('.file-name-formatter .sf-metadata-file-name');
|
||||
if (filenameElement) {
|
||||
filenameElement.addEventListener('click', handleClickFilename);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (filenameElement) {
|
||||
filenameElement.removeEventListener('click', handleClickFilename);
|
||||
}
|
||||
};
|
||||
}, [handleClickFilename]);
|
||||
const handleClickFilename = useCallback((event) => {
|
||||
event.stopPropagation();
|
||||
event.nativeEvent.stopImmediatePropagation();
|
||||
onOpenFile(record);
|
||||
}, [record, onOpenFile]);
|
||||
|
||||
return (
|
||||
<article
|
||||
ref={cardRef}
|
||||
data-id={record._id}
|
||||
className={classnames('sf-metadata-kanban-card', { 'readonly': readonly })}
|
||||
className={classnames('sf-metadata-kanban-card', { 'selected': isSelected })}
|
||||
onClick={handleClickCard}
|
||||
>
|
||||
{titleColumn && (
|
||||
<div className="sf-metadata-kanban-card-header">
|
||||
<div className="sf-metadata-kanban-card-header" onClick={handleClickFilename}>
|
||||
<Formatter value={titleValue} column={titleColumn} record={record}/>
|
||||
</div>
|
||||
)}
|
||||
<div className="sf-metadata-kanban-card-body">
|
||||
{displayColumns.map((column, index) => {
|
||||
{displayColumns.map((column) => {
|
||||
const value = getCellValueByColumn(record, column);
|
||||
if (!displayEmptyValue && !isValidCellValue(value)) {
|
||||
if (displayColumnName) {
|
||||
@@ -118,14 +69,14 @@ const Card = ({
|
||||
};
|
||||
|
||||
Card.propTypes = {
|
||||
readonly: PropTypes.bool,
|
||||
isSelected: PropTypes.bool,
|
||||
displayEmptyValue: PropTypes.bool,
|
||||
displayColumnName: PropTypes.bool,
|
||||
record: PropTypes.object,
|
||||
titleColumn: PropTypes.object,
|
||||
displayColumns: PropTypes.array,
|
||||
onCloseSettings: PropTypes.func.isRequired,
|
||||
onOpenFile: PropTypes.func.isRequired,
|
||||
onSelectCard: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Card;
|
||||
|
@@ -6,6 +6,7 @@ import { useMetadataView } from '../../../../hooks/metadata-view';
|
||||
import { getRowById } from '../../../../utils/table';
|
||||
import Container from '../../dnd/container';
|
||||
import Draggable from '../../dnd/draggable';
|
||||
import { geRecordIdFromRecord } from '../../../../utils/cell';
|
||||
|
||||
import './index.css';
|
||||
|
||||
@@ -19,12 +20,13 @@ const Board = ({
|
||||
groupByColumn,
|
||||
titleColumn,
|
||||
displayColumns,
|
||||
selectedCard,
|
||||
onMove,
|
||||
deleteOption,
|
||||
onFreezed,
|
||||
onUnFreezed,
|
||||
onCloseSettings,
|
||||
onOpenFile,
|
||||
onSelectCard,
|
||||
}) => {
|
||||
const [isDraggingOver, setDraggingOver] = useState(false);
|
||||
const boardName = useMemo(() => `sf_metadata_kanban_board_${board.key}`, [board]);
|
||||
@@ -75,17 +77,18 @@ const Board = ({
|
||||
{board.children.map((cardKey) => {
|
||||
const record = getRowById(metadata, cardKey);
|
||||
if (!record) return null;
|
||||
const isSelected = selectedCard === geRecordIdFromRecord(record);
|
||||
const CardElement = (
|
||||
<Card
|
||||
key={cardKey}
|
||||
readonly={readonly}
|
||||
isSelected={isSelected}
|
||||
displayEmptyValue={displayEmptyValue}
|
||||
displayColumnName={displayColumnName}
|
||||
record={record}
|
||||
titleColumn={titleColumn}
|
||||
displayColumns={displayColumns}
|
||||
onCloseSettings={onCloseSettings}
|
||||
onOpenFile={onOpenFile}
|
||||
onSelectCard={onSelectCard}
|
||||
/>
|
||||
);
|
||||
if (readonly) return CardElement;
|
||||
@@ -110,12 +113,13 @@ Board.propTypes = {
|
||||
groupByColumn: PropTypes.object,
|
||||
titleColumn: PropTypes.object,
|
||||
displayColumns: PropTypes.array,
|
||||
selectedCard: PropTypes.string,
|
||||
onMove: PropTypes.func,
|
||||
deleteOption: PropTypes.func,
|
||||
onFreezed: PropTypes.func,
|
||||
onUnFreezed: PropTypes.func,
|
||||
onCloseSettings: PropTypes.func.isRequired,
|
||||
onOpenFile: PropTypes.func.isRequired,
|
||||
onSelectCard: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Board;
|
||||
|
@@ -1,29 +1,33 @@
|
||||
import React, { useMemo, useCallback, useState } from 'react';
|
||||
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 { useCollaborators } from '../../../hooks';
|
||||
import { CellType, KANBAN_SETTINGS_KEYS, UNCATEGORIZED, FILE_TYPE } from '../../../constants';
|
||||
import { CellType, KANBAN_SETTINGS_KEYS, UNCATEGORIZED } from '../../../constants';
|
||||
import { COLUMN_DATA_OPERATION_TYPE } from '../../../store/operations';
|
||||
import { gettext, siteRoot } from '../../../../utils/constants';
|
||||
import { checkIsPredefinedOption, getCellValueByColumn, isValidCellValue, geRecordIdFromRecord } from '../../../utils/cell';
|
||||
import { gettext } from '../../../../utils/constants';
|
||||
import { checkIsPredefinedOption, getCellValueByColumn, isValidCellValue, geRecordIdFromRecord,
|
||||
getFileNameFromRecord, getParentDirFromRecord
|
||||
} from '../../../utils/cell';
|
||||
import { getColumnOptions, getColumnOriginName } from '../../../utils/column';
|
||||
import AddBoard from '../add-board';
|
||||
import EmptyTip from '../../../../components/empty-tip';
|
||||
import Board from './board';
|
||||
import { Utils } from '../../../../utils/utils';
|
||||
import { EVENT_BUS_TYPE } from '../../../../components/common/event-bus-type';
|
||||
import ImagePreviewer from '../../../components/cell-formatter/image-previewer';
|
||||
import { getRowById } from '../../../utils/table';
|
||||
import { openFile } from '../../../utils/open-file';
|
||||
import { checkIsDir } from '../../../utils/row';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const Boards = ({ modifyRecord, modifyColumnData, onCloseSettings }) => {
|
||||
const [haveFreezed, setHaveFreezed] = useState(false);
|
||||
const [isImagePreviewerVisible, setImagePreviewerVisible] = useState(false);
|
||||
const [record, setRecord] = useState(null);
|
||||
const [selectedCard, setSelectedCard] = useState('');
|
||||
|
||||
const { metadata, store } = useMetadataView();
|
||||
const currentImageRef = useRef(null);
|
||||
const containerRef = useRef(null);
|
||||
|
||||
const { isDirentDetailShow, metadata, store, updateCurrentDirent, showDirentDetail } = useMetadataView();
|
||||
const { collaborators } = useCollaborators();
|
||||
|
||||
const groupByColumn = useMemo(() => {
|
||||
@@ -170,68 +174,57 @@ const Boards = ({ modifyRecord, modifyColumnData, onCloseSettings }) => {
|
||||
setHaveFreezed(false);
|
||||
}, []);
|
||||
|
||||
const isEmpty = boards.length === 0;
|
||||
|
||||
const generateUrl = (fileName, parentDir) => {
|
||||
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||
const path = Utils.encodePath(Utils.joinPath(parentDir, fileName));
|
||||
return `${siteRoot}lib/${repoID}/file${path}`;
|
||||
};
|
||||
|
||||
const openMarkdown = useCallback((name, parentDir) => {
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.OPEN_MARKDOWN_DIALOG, parentDir, name);
|
||||
}, []);
|
||||
|
||||
const openByNewWindow = useCallback((fileType, fileName, parentDir) => {
|
||||
if (!fileType) {
|
||||
const url = generateUrl(fileName, parentDir);
|
||||
window.open(url);
|
||||
} else {
|
||||
let pathname = window.location.pathname;
|
||||
if (pathname.endsWith('/')) {
|
||||
pathname = pathname.slice(0, -1);
|
||||
}
|
||||
window.open(window.location.origin + pathname + Utils.encodePath(Utils.joinPath(parentDir, fileName)));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const openSdoc = useCallback((fileName, parentDir) => {
|
||||
const url = generateUrl(fileName, parentDir);
|
||||
window.open(url);
|
||||
}, []);
|
||||
|
||||
const openFile = useCallback((type, fileName, parentDir, recordId = null) => {
|
||||
switch (type) {
|
||||
case FILE_TYPE.MARKDOWN: {
|
||||
openMarkdown(fileName, parentDir);
|
||||
break;
|
||||
}
|
||||
case FILE_TYPE.SDOC: {
|
||||
openSdoc(fileName, parentDir);
|
||||
break;
|
||||
}
|
||||
case FILE_TYPE.IMAGE: {
|
||||
const record = getRowById(metadata, recordId);
|
||||
setRecord(record);
|
||||
const onOpenFile = useCallback((record) => {
|
||||
openFile(record, window.sfMetadataContext.eventBus, () => {
|
||||
currentImageRef.current = record;
|
||||
setImagePreviewerVisible(true);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
openByNewWindow(type, fileName, parentDir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, [openMarkdown, openSdoc, openByNewWindow, metadata]);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const closeImagePreviewer = useCallback(() => {
|
||||
currentImageRef.current = null;
|
||||
setImagePreviewerVisible(false);
|
||||
}, []);
|
||||
|
||||
const onSelectCard = useCallback((record) => {
|
||||
const recordId = geRecordIdFromRecord(record);
|
||||
const name = getFileNameFromRecord(record);
|
||||
const path = getParentDirFromRecord(record);
|
||||
const isDir = checkIsDir(record);
|
||||
updateCurrentDirent({
|
||||
type: isDir ? 'dir' : 'file',
|
||||
mtime: '',
|
||||
name,
|
||||
path,
|
||||
file_tags: []
|
||||
});
|
||||
setSelectedCard(recordId);
|
||||
onCloseSettings();
|
||||
showDirentDetail();
|
||||
}, [onCloseSettings, showDirentDetail, updateCurrentDirent]);
|
||||
|
||||
const handleClickOutside = useCallback((event) => {
|
||||
if (!containerRef.current.contains(event.target)) return;
|
||||
setSelectedCard(null);
|
||||
updateCurrentDirent();
|
||||
}, [updateCurrentDirent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDirentDetailShow) {
|
||||
setSelectedCard(null);
|
||||
}
|
||||
}, [isDirentDetailShow]);
|
||||
|
||||
const isEmpty = boards.length === 0;
|
||||
|
||||
return (
|
||||
<div className={classnames('sf-metadata-view-kanban-boards', {
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={classnames('sf-metadata-view-kanban-boards', {
|
||||
'sf-metadata-view-kanban-boards-text-wrap': textWrap,
|
||||
'readonly': readonly,
|
||||
})}
|
||||
onClick={handleClickOutside}
|
||||
>
|
||||
<div className="smooth-dnd-container horizontal">
|
||||
{isEmpty && (<EmptyTip className="tips-empty-boards" text={gettext('No categories')} />)}
|
||||
@@ -250,12 +243,13 @@ const Boards = ({ modifyRecord, modifyColumnData, onCloseSettings }) => {
|
||||
groupByColumn={groupByColumn}
|
||||
titleColumn={titleColumn}
|
||||
displayColumns={displayColumns}
|
||||
selectedCard={selectedCard}
|
||||
onMove={onMove}
|
||||
deleteOption={deleteOption}
|
||||
onFreezed={onFreezed}
|
||||
onUnFreezed={onUnFreezed}
|
||||
onCloseSettings={onCloseSettings}
|
||||
onOpenFile={openFile}
|
||||
onOpenFile={onOpenFile}
|
||||
onSelectCard={onSelectCard}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -263,7 +257,7 @@ const Boards = ({ modifyRecord, modifyColumnData, onCloseSettings }) => {
|
||||
)}
|
||||
{!readonly && (<AddBoard groupByColumn={groupByColumn}/>)}
|
||||
</div>
|
||||
{isImagePreviewerVisible && (<ImagePreviewer record={record} table={metadata} closeImagePopup={closeImagePreviewer} />)}
|
||||
{isImagePreviewerVisible && (<ImagePreviewer record={currentImageRef.current} table={metadata} closeImagePopup={closeImagePreviewer} />)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@@ -45,8 +45,10 @@ const Kanban = () => {
|
||||
useEffect(() => {
|
||||
const eventBus = window.sfMetadataContext.eventBus;
|
||||
const unsubscribeKanbanSetting = eventBus.subscribe(EVENT_BUS_TYPE.TOGGLE_KANBAN_SETTINGS, () => setShowSettings(!isShowSettings));
|
||||
const unsubscribeCloseKanbanSetting = eventBus.subscribe(EVENT_BUS_TYPE.CLOSE_KANBAN_SETTINGS, () => setShowSettings(false));
|
||||
return () => {
|
||||
unsubscribeKanbanSetting();
|
||||
unsubscribeCloseKanbanSetting();
|
||||
};
|
||||
}, [isShowSettings]);
|
||||
|
||||
|
@@ -1,121 +1,28 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { UncontrolledTooltip } from 'reactstrap';
|
||||
import { IconBtn } from '@seafile/sf-metadata-ui-component';
|
||||
import { Utils } from '../../../../../../../../utils/utils';
|
||||
import { gettext, siteRoot } from '../../../../../../../../utils/constants';
|
||||
import { EVENT_BUS_TYPE } from '../../../../../../../..//components/common/event-bus-type';
|
||||
import { gettext } from '../../../../../../../../utils/constants';
|
||||
import { EVENT_BUS_TYPE as METADATA_EVENT_BUS_TYPE, EDITOR_TYPE } from '../../../../../../../constants';
|
||||
import { getFileNameFromRecord, getParentDirFromRecord } from '../../../../../../../utils/cell';
|
||||
import { checkIsDir } from '../../../../../../../utils/row';
|
||||
import { openFile } from '../../../../../../../utils/open-file';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const FILE_TYPE = {
|
||||
FOLDER: 'folder',
|
||||
MARKDOWN: 'markdown',
|
||||
SDOC: 'sdoc',
|
||||
IMAGE: 'image',
|
||||
};
|
||||
|
||||
const CellOperationBtn = ({ isDir, column, record, cellValue, ...props }) => {
|
||||
|
||||
const fileName = useMemo(() => {
|
||||
const { key } = column;
|
||||
return record[key];
|
||||
}, [column, record]);
|
||||
|
||||
const fileType = useMemo(() => {
|
||||
if (checkIsDir(record)) return FILE_TYPE.FOLDER;
|
||||
if (!fileName) return '';
|
||||
const index = fileName.lastIndexOf('.');
|
||||
if (index === -1) return '';
|
||||
const suffix = fileName.slice(index).toLowerCase();
|
||||
if (suffix.indexOf(' ') > -1) return '';
|
||||
if (Utils.imageCheck(fileName)) return FILE_TYPE.IMAGE;
|
||||
if (Utils.isMarkdownFile(fileName)) return FILE_TYPE.MARKDOWN;
|
||||
if (Utils.isSdocFile(fileName)) return FILE_TYPE.SDOC;
|
||||
return '';
|
||||
}, [record, fileName]);
|
||||
|
||||
const getParentDir = () => {
|
||||
const parentDir = getParentDirFromRecord(record);
|
||||
if (parentDir === '/') {
|
||||
return '';
|
||||
}
|
||||
return parentDir;
|
||||
};
|
||||
|
||||
const generateUrl = () => {
|
||||
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||
const parentDir = getParentDir();
|
||||
const path = Utils.encodePath(Utils.joinPath(parentDir, fileName));
|
||||
return `${siteRoot}lib/${repoID}/file${path}`;
|
||||
};
|
||||
|
||||
const openUrl = (url) => {
|
||||
window.open(url);
|
||||
};
|
||||
|
||||
const openMarkdown = () => {
|
||||
const fileName = getFileNameFromRecord(record);
|
||||
const parentDir = getParentDirFromRecord(record);
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.OPEN_MARKDOWN_DIALOG, parentDir, fileName);
|
||||
};
|
||||
|
||||
const openByNewWindow = (fileType) => {
|
||||
if (!fileType) {
|
||||
const url = generateUrl();
|
||||
openUrl(url);
|
||||
} else {
|
||||
const parentDir = getParentDir();
|
||||
let pathname = window.location.pathname;
|
||||
if (pathname.endsWith('/')) {
|
||||
pathname = pathname.slice(0, -1);
|
||||
}
|
||||
openUrl(window.location.origin + pathname + Utils.encodePath(Utils.joinPath(parentDir, fileName)));
|
||||
}
|
||||
};
|
||||
|
||||
const openSdoc = () => {
|
||||
const url = generateUrl();
|
||||
openUrl(url);
|
||||
};
|
||||
|
||||
const openOthers = () => {
|
||||
openByNewWindow(fileType);
|
||||
};
|
||||
|
||||
const openFile = (event) => {
|
||||
const handelClick = (event) => {
|
||||
event.stopPropagation();
|
||||
event.nativeEvent.stopImmediatePropagation();
|
||||
|
||||
switch (fileType) {
|
||||
case FILE_TYPE.MARKDOWN: {
|
||||
openMarkdown();
|
||||
break;
|
||||
}
|
||||
case FILE_TYPE.SDOC: {
|
||||
openSdoc();
|
||||
break;
|
||||
}
|
||||
case FILE_TYPE.IMAGE: {
|
||||
// render image previewer via FileNameEditor
|
||||
openFile(record, window.sfMetadataContext.eventBus, () => {
|
||||
window.sfMetadataContext.eventBus.dispatch(METADATA_EVENT_BUS_TYPE.OPEN_EDITOR, EDITOR_TYPE.PREVIEWER);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
openOthers();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (!cellValue) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconBtn id={'sf-metadata-cell-open-file-btn'} className="sf-metadata-cell-operation-btn" size={20} iconName="open-file" onClick={openFile} />
|
||||
<IconBtn id={'sf-metadata-cell-open-file-btn'} className="sf-metadata-cell-operation-btn" size={20} iconName="open-file" onClick={handelClick} />
|
||||
<UncontrolledTooltip
|
||||
hideArrow
|
||||
target={'sf-metadata-cell-open-file-btn'}
|
||||
|
@@ -2358,6 +2358,7 @@ class LibContentView extends React.Component {
|
||||
<DirColumnView
|
||||
isSidePanelFolded={this.props.isSidePanelFolded}
|
||||
isTreePanelShown={this.state.isTreePanelShown}
|
||||
isDirentDetailShow={this.state.isDirentDetailShow}
|
||||
currentMode={this.state.currentMode}
|
||||
path={this.state.path}
|
||||
repoID={this.props.repoID}
|
||||
|
Reference in New Issue
Block a user