1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-05-11 09:24:38 +00:00

Merge pull request from haiwen/optimize/file_name_in_table_view

Optimize/file name in table view
This commit is contained in:
杨国璇 2025-02-07 14:00:44 +08:00 committed by GitHub
commit a87e662f55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 37 additions and 108 deletions
frontend
package-lock.jsonpackage.json
src/metadata
components
cell-formatter
context-menu
views
kanban/boards/board
table/table-main/records/record/cell
formatter.jsindex.cssindex.js
operation-btn
file-name-operation-btn
index.js

View File

@ -19,7 +19,7 @@
"@seafile/sdoc-editor": "1.0.212",
"@seafile/seafile-calendar": "0.0.28",
"@seafile/seafile-editor": "1.0.133",
"@seafile/sf-metadata-ui-component": "^0.0.64",
"@seafile/sf-metadata-ui-component": "^0.0.68",
"@seafile/stldraw-editor": "0.1.5",
"@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/codemirror-themes": "^4.23.5",
@ -5410,9 +5410,9 @@
}
},
"node_modules/@seafile/sf-metadata-ui-component": {
"version": "0.0.64",
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.64.tgz",
"integrity": "sha512-C4kLqsZCg+KutvOzsv65j58x7PQ6t8wPRJwXIBmEBSqB0/suZZnEV6uNhU5W83zN2CzHmGeRvg4+v+/Pj4pEiA==",
"version": "0.0.68",
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.68.tgz",
"integrity": "sha512-DTBEyPxj1TNSBNkv+VAidt9x2/KQwHVKF0wbxHGEFxXR/OxX3S7bbqMpf9/FtUVIeEPjk8xPTHs4jYEDN6MY3Q==",
"dependencies": {
"@seafile/seafile-calendar": "0.0.28",
"@seafile/seafile-editor": "^1.0.133",

View File

@ -14,7 +14,7 @@
"@seafile/sdoc-editor": "1.0.212",
"@seafile/seafile-calendar": "0.0.28",
"@seafile/seafile-editor": "1.0.133",
"@seafile/sf-metadata-ui-component": "^0.0.64",
"@seafile/sf-metadata-ui-component": "^0.0.68",
"@seafile/stldraw-editor": "0.1.5",
"@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/codemirror-themes": "^4.23.5",

View File

@ -7,7 +7,7 @@ import { siteRoot, thumbnailDefaultSize } from '../../../utils/constants';
import { getParentDirFromRecord } from '../../utils/cell';
import { checkIsDir } from '../../utils/row';
const FileName = ({ record, className: propsClassName, value, ...params }) => {
const FileName = ({ record, className: propsClassName, value, onFileNameClick, ...params }) => {
const parentDir = useMemo(() => getParentDirFromRecord(record), [record]);
const isDir = useMemo(() => checkIsDir(record), [record]);
const className = useMemo(() => {
@ -30,7 +30,7 @@ const FileName = ({ record, className: propsClassName, value, ...params }) => {
return { iconUrl: defaultIconUrl, defaultIconUrl };
}, [isDir, value, parentDir]);
return (<FileNameFormatter { ...params } className={className} value={value} { ...iconUrl } />);
return (<FileNameFormatter { ...params } className={className} value={value} onClickName={onFileNameClick} { ...iconUrl } />);
};
@ -38,6 +38,7 @@ FileName.propTypes = {
value: PropTypes.string,
record: PropTypes.object.isRequired,
className: PropTypes.string,
onFileNameClick: PropTypes.func,
};
export default FileName;

View File

@ -96,7 +96,7 @@ const ContextMenu = ({
>
{options.map((option, index) => {
if (option === 'Divider') {
return <DropdownItem key="divider-item" divider />;
return <DropdownItem key={index} divider />;
} else {
return (
<button
@ -114,10 +114,13 @@ const ContextMenu = ({
};
ContextMenu.propTypes = {
options: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
})).isRequired,
options: PropTypes.arrayOf(PropTypes.oneOfType([
PropTypes.shape({
value: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
}),
PropTypes.string
])).isRequired,
boundaryCoordinates: PropTypes.object,
ignoredTriggerElements: PropTypes.array,
onOptionClick: PropTypes.func.isRequired,

View File

@ -36,15 +36,8 @@
overflow: hidden;
}
.sf-metadata-kanban-card .sf-metadata-kanban-card-header .sf-metadata-file-name {
text-decoration: none;
flex: unset;
}
.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 {

View File

@ -4,7 +4,6 @@ import classnames from 'classnames';
import Formatter from '../formatter';
import { getCellValueByColumn, isValidCellValue } from '../../../../../utils/cell';
import { CellType } from '../../../../../constants';
import { getEventClassName } from '../../../../../utils/common';
import './index.css';
@ -28,10 +27,8 @@ const Card = ({
onSelectCard(record);
}, [record, onSelectCard]);
const handleClickFilename = useCallback((event) => {
const handleFilenameClick = useCallback((event) => {
if (titleColumn?.type !== CellType.FILE_NAME) return;
const eventName = getEventClassName(event);
if (eventName !== 'sf-metadata-file-name') return;
event.stopPropagation();
event.nativeEvent.stopImmediatePropagation();
onOpenFile(record);
@ -45,8 +42,8 @@ const Card = ({
onContextMenu={onContextMenu}
>
{titleColumn && (
<div className="sf-metadata-kanban-card-header" onClick={handleClickFilename}>
<Formatter value={titleValue} column={titleColumn} record={record}/>
<div className="sf-metadata-kanban-card-header">
<Formatter value={titleValue} column={titleColumn} record={record} onFileNameClick={handleFilenameClick} />
</div>
)}
<div className="sf-metadata-kanban-card-body">

View File

@ -14,7 +14,7 @@ const SPECIAL_FILE_ICON = [
'word.png',
];
const Formatter = ({ value, column, record }) => {
const Formatter = ({ value, column, record, ...params }) => {
let className = '';
if (column.type === CellType.FILE_NAME && value) {
@ -24,7 +24,7 @@ const Formatter = ({ value, column, record }) => {
}
}
return (<CellFormatter readonly={true} className={className} value={value} field={column} record={record} />);
return (<CellFormatter { ...params } readonly={true} className={className} value={value} field={column} record={record} />);
};
Formatter.propTypes = {

View File

@ -6,7 +6,7 @@ import RateEditor from '../../../../../../components/cell-editors/rate-editor';
import { canEditCell } from '../../../../../../utils/column';
import { CellType } from '../../../../../../constants';
const Formatter = ({ isCellSelected, field, value, onChange, record }) => {
const Formatter = ({ isCellSelected, field, value, onChange, record, ...params }) => {
const { type } = field;
const cellEditAble = canEditCell(field, record, true);
if (type === CellType.CHECKBOX && cellEditAble) {
@ -16,7 +16,7 @@ const Formatter = ({ isCellSelected, field, value, onChange, record }) => {
return (<RateEditor isCellSelected={isCellSelected} value={value} field={field} onChange={onChange} />);
}
return (<CellFormatter readonly={true} isCellSelected={isCellSelected} value={value} field={field} record={record} />);
return (<CellFormatter { ...params } readonly={true} value={value} field={field} record={record} />);
};
Formatter.propTypes = {

View File

@ -308,6 +308,7 @@
.sf-metadata-result-table-cell.sf-metadata-result-table-file-name-cell {
padding-top: 4px;
padding-bottom: 4px;
cursor: pointer;
}
.sf-metadata-result-table-cell.sf-metadata-result-table-file-name-cell .sf-metadata-ui.cell-formatter-container {

View File

@ -6,8 +6,9 @@ import CellOperationBtn from './operation-btn';
import { Utils } from '../../../../../../../utils/utils';
import ObjectUtils from '../../../../../../utils/object-utils';
import { isCellValueChanged, getCellValueByColumn } from '../../../../../../utils/cell';
import { CellType, PRIVATE_COLUMN_KEYS, TABLE_SUPPORT_EDIT_TYPE_MAP } from '../../../../../../constants';
import { CellType, PRIVATE_COLUMN_KEYS, TABLE_SUPPORT_EDIT_TYPE_MAP, EDITOR_TYPE, EVENT_BUS_TYPE } from '../../../../../../constants';
import { checkIsDir } from '../../../../../../utils/row';
import { openFile } from '../../../../../../utils/file';
import './index.css';
@ -148,6 +149,16 @@ const Cell = React.memo(({
cellMetaData.modifyRecord({ rowId, cellKey: columnKey, updates, originalUpdates: updated, oldRowData, originalOldRowData });
}, [cellMetaData, record, column, getOldRowData]);
const onFileNameClick = useCallback((event) => {
event.preventDefault();
event.nativeEvent.stopImmediatePropagation();
if (!isCellSelected) return;
const repoID = window.sfMetadataContext.getSetting('repoID');
openFile(repoID, record, () => {
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.OPEN_EDITOR, EDITOR_TYPE.PREVIEWER);
});
}, [isCellSelected, record]);
const cellValue = getCellValueByColumn(record, column);
const cellEvents = needBindEvents && getEvents();
const containerProps = {
@ -158,7 +169,7 @@ const Cell = React.memo(({
return (
<div key={`${record._id}-${column.key}`} {...containerProps}>
<Formatter isCellSelected={isCellSelected} value={cellValue} field={column} onChange={modifyRecord} record={record} />
<Formatter isCellSelected={isCellSelected} value={cellValue} field={column} onChange={modifyRecord} record={record} onFileNameClick={onFileNameClick} />
{isCellSelected && (<CellOperationBtn record={record} column={column}/>)}
</div>
);

View File

@ -1,18 +0,0 @@
.sf-metadata-cell-operation-btn {
background: #fff;
border-radius: 3px;
box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.1) 0px 2px 4px;
position: absolute;
right: 8px;
top: 6px;
transition: background 0.020s ease-in;
}
.sf-metadata-cell-operation-btn:hover {
background: #efefef;
}
.sf-metadata-cell-operation-btn .sf-metadata-icon {
fill: #666;
font-size: 14px;
}

View File

@ -1,55 +0,0 @@
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { UncontrolledTooltip } from 'reactstrap';
import { IconBtn } from '@seafile/sf-metadata-ui-component';
import { gettext } from '../../../../../../../../../utils/constants';
import { EVENT_BUS_TYPE as METADATA_EVENT_BUS_TYPE, EDITOR_TYPE } from '../../../../../../../../constants';
import { checkIsDir } from '../../../../../../../../utils/row';
import { openFile } from '../../../../../../../../utils/file';
import './index.css';
const FileNameOperationBtn = ({ column, record, ...props }) => {
const fileName = useMemo(() => {
const { key } = column;
return record[key];
}, [column, record]);
const isDir = useMemo(() => checkIsDir(record), [record]);
const handelClick = (event) => {
event.stopPropagation();
event.nativeEvent.stopImmediatePropagation();
const repoID = window.sfMetadataContext.getSetting('repoID');
openFile(repoID, record, () => {
window.sfMetadataContext.eventBus.dispatch(METADATA_EVENT_BUS_TYPE.OPEN_EDITOR, EDITOR_TYPE.PREVIEWER);
});
};
if (!fileName) return null;
return (
<>
<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"
placement="bottom"
fade={false}
delay={{ show: 0, hide: 0 }}
modifiers={{ preventOverflow: { boundariesElement: document.body } }}
className="sf-metadata-tooltip"
>
{isDir ? gettext('Open folder') : gettext('Open file')}
</UncontrolledTooltip>
</>
);
};
FileNameOperationBtn.propTypes = {
column: PropTypes.object,
record: PropTypes.object,
};
export default FileNameOperationBtn;

View File

@ -1,14 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import { CellType } from '../../../../../../../constants';
import FileNameOperationBtn from './file-name-operation-btn';
import LinkOperationBtn from './link-operation-btn';
const CellOperationBtn = ({ column, record }) => {
switch (column.type) {
case CellType.FILE_NAME: {
return (<FileNameOperationBtn column={column} record={record} />);
}
case CellType.LINK: {
return (<LinkOperationBtn column={column} record={record} />);
}